Rust Trait对象转换

Jak*_*sel 3 type-conversion coercion traits rust

由于此错误的两个实例,以下代码将无法编译:

错误[E0277]:Self: std::marker::Sized不满足特征限制

我不明白为什么Sized在这个实例中需要&self&Any作为指针并且操作不需要知道实现特征的结构的大小,它只需要知道指针本身及其转换的类型和它将具有,因为&self在特征内部实现时是通用的.

我认为这可能是编译器强制执行不必要的约束的一个实例,我考虑过使用生锈的GitHub仓库提出问题,但我想我应该看看,在我提出问题之前,有人在这里知道我不知道的事情.

use std::any::Any;

trait Component: Any {
    fn as_any(&self) -> &Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut Any {
        self
    }
}
Run Code Online (Sandbox Code Playgroud)

替代方法是为实现此特征的结构创建as_any()as_any_mut()需要函数,但对于那些结构,实现将始终完全按照此处显示到每个单独的字符,从而生成相同样板代码的多个实例.

Fra*_*gné 6

动态大小的类型也可以实现特征.特别是,当您定义对象安全特征时,编译器还定义了一个动态大小的类型,其名称与特征相同,这使您可以使用对象类型,如&Component.

对象类型,例如&Component&Any不仅仅是普通的指针; 他们是胖子.胖指针结合了指向数据的指针和另一个数据:对于对象类型,它是指向vtable的指针; 对于切片,它是切片的长度.

当从常规指针(例如a &Button)转换为对象类型时,编译器静态地知道将哪个vtable放入胖指针(例如Button,vtable Any).另一方面,Rust不支持从对象类型转换为另一个对象类型(例如从&Componentto到&Any),因为对象中没有足够的数据来初始化新的fat指针.这就是编译器将此注释添加到错误消息的原因:

= note: required for the cast to the object type `std::any::Any + 'static`
Run Code Online (Sandbox Code Playgroud)

有两种方法可以解决这个问题:

  1. 要求所有类型的实现ComponentSized:

    trait Component: Any + Sized {
        fn as_any(&self) -> &Any {
            self
        }
    
        fn as_any_mut(&mut self) -> &mut Any {
            self
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这导致您将无法使用诸如&Component或根本不使用对象类型Box<Component>.

  2. 使得as_anyas_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
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这样,你仍然可以使用对象类型为特质,但你不能打电话as_anyas_any_mut他们.


Jak*_*sel 5

我发现我认为是一个很好的解决方案,不需要新的编译器功能。

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
    }
}
Run Code Online (Sandbox Code Playgroud)

从这里开始,我只是将所有 API 更改为接受ComponentAny而不是Component. 因为Any为任何'static类型自动实现,ComponentAny现在为任何'static实现Component. 感谢有没有办法组合多个特征来定义一个新特征?为了这个想法。