了解特征和对象安全

Understanding Traits and Object Safety

我正在努力学习对象安全的基础知识。如果我有这个代码

struct S {
    x: i32,
}

trait Trait: Sized {
    fn f(&self) -> i32
    where
        Self: Sized;
}

fn object_safety_dynamic(x: Trait) {}

我收到

error[E0038]: the trait `Trait` cannot be made into an object
  --> src/lib.rs:11:29
   |
5  | trait Trait: Sized {
   |       -----  ----- ...because it requires `Self: Sized`
   |       |
   |       this trait cannot be made into an object...
...
11 | fn object_safety_dynamic(x: Trait) {}
   |                             ^^^^^ the trait `Trait` cannot be made into an object

当我添加或删除 : Sized 作为超特性或 f 的绑定时,我收到略有不同的错误消息。

谁能解释一下:

如果重要的话,我正在使用 rustc 1.19.0-nightly (01951a61a 2017-05-20)

解决关于固定尺寸的评论。

trait TraitB {
    fn f(&self) -> i32
    where
        Self: Sized;

    fn g<T>(&self, t: T) -> i32
    where
        Self: Sized;
}

使 Trait 成为 Sized 的超类型没有帮助 - 事实上,正如错误消息所说,这是不允许的。 Trait 的每个实现仍将具有 不同的 大小,因此您的函数 object_safety_dynamic 无法编译。此处不能使用单态化,因为没有泛型参数,因此编译后的函数必须适用于 Trait.

all 实现

但是,引用 do 具有固定大小,因此将参数转换为引用是可行的:

trait Trait {
    fn f(&self) -> i32;
}

fn object_safety_dynamic(x: &Trait) {}

特征对象总是某种引用,例如一个 Box<T>&T。这正是因为特征实现的大小会有所不同,而引用类型具有已知的固定大小。

Why does this particular example not work? The chapter Trait Objects states:

So what makes a method object-safe? Each method must require that Self: Sized

这不是应验了吗?

这个问题真的是:什么是特征对象?

一个特征对象是面向对象范例中的一个接口:

  • 它公开了一组有限的方法,
  • 应用于未知的具体类型。

应用操作的具体类型是未知的,这就是人们使用 trait 对象的具体原因,因为它允许以统一的方式操纵一组异构类型 直至程序集等级.

然而,具体类型未知的事实意味着包含内存的内存区域的大小未知;因此,特征对象只能在 referencepointer 之后进行操作,例如 &dyn TraitObject&mut dyn TraitObjectBox<dyn TraitObject> 例如。

在内存级别,它们中的每一个都以相同的方式表示:

  • 指向虚拟 table 的指针,它是一种结构,在固定偏移量处为特征对象的每个“方法”保存一个函数指针,
  • 指向对象实际数据的指针。

What is the difference between Trait: Sized and where Self: Sized? (Well, yes, one inherits the trait the other one is a parameter bound, but from Rust's trait object perspective?)

Rust 中没有继承。在两种情况下,它们都是边界:

  • Trait: Sized 声明特征本身只能为已经实现 Sized,
  • 的类型实现
  • fn method(&self) where Self: Sized 声明只有实现 Sized 的类型才能实现此方法。

注意:实现trait时,所有的方法最终都必须有一个定义;因此后者只有在为具有 Self: Sized 绑定的方法提供默认实现时才真正有用,如 is shown here.

What is the preferred change I had to make object_safety_dynamic work?

您必须通过引用或指针获取特征对象。使用引用还是指针取决于是否要转移所有权。