Конвертация объекта 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`

Есть два способа исправить это:

  1. Требовать, чтобы все типы реализовывали Component быть Sized:

    trait Component: Any + Sized {
        fn as_any(&self) -> &Any {
            self
        }
    
        fn as_any_mut(&mut self) -> &mut Any {
            self
        }
    }
    

    Это приводит к тому, что вы не сможете использовать типы объектов, такие как &Component или же Box<Component> совсем.

  2. Сделать 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, Благодаря Есть ли способ объединить несколько черт, чтобы определить новую черту? для идеи.

Другие вопросы по тегам