【问题标题】:Rust Trait object conversionRust Trait 对象转换
【发布时间】:2017-05-27 00:35:54
【问题描述】:

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

error[E0277]: trait bound Self: std::marker::Sized 不满足

我不明白为什么在这种情况下需要Sized,因为&self&Any 都是指针,并且该操作不需要知道实现特征的结构的大小,它只需要知道指针本身和它正在转换的类型,因为&self 在 trait 中实现时是通用的。

我认为这可能是编译器强制执行不必要约束的一个实例,我已经考虑向 rust-lang GitHub 存储库提交问题,但我想我应该看看这里是否有人知道我不知道的事情,然后再去提出问题。

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() 所需的函数,但对于这些结构,实现将始终与此处显示的每个字符完全相同,从而导致多个实例相同的样板代码。

【问题讨论】:

  • 我删除了我的答案,因为我找不到足够快的文档链接。不过,我的理解是,traits 中的Self 是未调整大小的,并且如果没有将您的特征明确标记为Sized,编译器会将其视为未调整大小和错误。
  • 我认为制作所需的方法是要走的路。至少我以前见过这种模式。为避免重复代码,您可以编写一个简单的宏 impl_conversion_functions!() 或类似的东西。

标签: rust type-conversion traits coercion


【解决方案1】:

动态大小的类型也可以实现特征。特别是,当你定义一个对象安全的 trait 时,编译器还会定义一个与 trait 同名的动态大小的类型,这样你就可以使用 &Component 这样的对象类型。

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

当从常规指针(例如 &Button)转换为对象类型时,编译器静态地知道将哪个 vtable 放入胖指针(例如 Button 的 vtable 对应于 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
        }
    }
    

    这会导致您根本无法使用 &ComponentBox<Component> 等对象类型。

  2. 使as_anyas_any_mut 方法仅在SelfSized 时可用:

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

    这样,您仍然可以为 trait 使用对象类型,但您将无法对它们调用 as_anyas_any_mut

【讨论】:

  • 这是有道理的。不幸的是,对于我的特殊情况,我确实需要能够在特征对象上调用这些函数。我看看能不能找到别的方法
【解决方案2】:

我发现我认为不需要新编译器功能的出色解决方案。

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 现在会为任何实现Component'static 类型自动实现。感谢Is there a way to combine multiple traits in order to define a new trait? 的想法。

【讨论】:

    猜你喜欢
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多