Конвертация объекта Rust Trait
Следующий код не скомпилируется из-за двух случаев этой ошибки:
ошибка [E0277]: ограничение черты
Self: std::marker::Sized
не устраивает
Я не понимаю почему Sized
требуется в этом случае, так как оба &self
а также &Any
являются указателями, и операция не требует знания размера структуры, которая реализует признак, она требует только знания самого указателя и типа, из которого он преобразуется, в который он будет иметь &self
является общим, когда реализовано внутри черты.
Я думаю, что это может быть примером того, как компилятор навязывает ненужные ограничения, и я подумал о том, чтобы подать проблему с репозиторием GitHub от rust-lang, но я подумал, что, возможно, мне следует выяснить, знает ли кто-то здесь что-то, чего я не знаю, прежде чем подать проблему,
use std::any::Any;
trait Component: Any {
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
Альтернативой этому является сделать as_any()
а также as_any_mut()
требуемые функции для структур, которые реализуют эту черту, но для этих структур реализация всегда будет точно такой, как показано здесь для каждого отдельного символа, что приводит к нескольким экземплярам идентичного стандартного кода.
2 ответа
Динамические типы также могут реализовывать черты. В частности, когда вы определяете объектно-безопасную черту, компилятор также определяет динамически изменяемый тип с тем же именем, что и черта, что позволяет вам использовать типы объектов, такие как &Component
,
Типы объектов, такие как &Component
или же &Any
не просто обычные указатели; они жирные указатели. Толстый указатель объединяет указатель на данные и другой фрагмент данных: для типов объектов это указатель на vtable; для ломтиков это длина ломтика.
При приведении из обычного указателя (например, &Button
) к типу объекта компилятор статически знает, какую vtable поместить в толстый указатель (например, Button
жизнеспособен для Any
). С другой стороны, Rust не поддерживает приведение типов объекта к другому типу объекта (например, из &Component
в &Any
), поскольку в объекте недостаточно данных для инициализации нового жирного указателя. Вот почему компилятор добавляет эту заметку к сообщению об ошибке:
= note: required for the cast to the object type `std::any::Any + 'static`
Есть два способа исправить это:
Требовать, чтобы все типы реализовывали
Component
бытьSized
:trait Component: Any + Sized { fn as_any(&self) -> &Any { self } fn as_any_mut(&mut self) -> &mut Any { self } }
Это приводит к тому, что вы не сможете использовать типы объектов, такие как
&Component
или жеBox<Component>
совсем.Сделать
as_any
а такжеas_any_mut
методы доступны только тогда, когдаSelf
являетсяSized
:trait Component: Any { fn as_any(&self) -> &Any where Self: Sized { self } fn as_any_mut(&mut self) -> &mut Any where Self: Sized { self } }
Таким образом, вы все еще можете использовать типы объектов для черты, но вы не сможете вызвать
as_any
а такжеas_any_mut
на них.
Я нашел то, что считаю отличным решением, не требующим новых возможностей компилятора.
pub trait Component {
// ...
}
pub trait ComponentAny: Component + Any {
fn as_any(&self) -> &Any;
fn as_any_mut(&mut self) -> &mut Any;
}
impl<T> ComponentAny for T
where T: Component + Any
{
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
Отсюда я просто изменяю все свои API, чтобы принимать ComponentAny
вместо Component
, Так как Any
автоматически реализуется для любого 'static
тип, ComponentAny
теперь автоматически реализуется для любого 'static
тип, который реализует Component
, Благодаря Есть ли способ объединить несколько черт, чтобы определить новую черту? для идеи.