8

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

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绑定时,我收到略有不同的错误消息。

有人可以解释一下:

  • 为什么这个特定的例子不起作用?特征对象一章指出:

    那么是什么让方法对象安全呢?每种方法都必须要求Self: Sized

    这不是实现了吗?

  • Trait: Sized和 和有什么不一样where Self: Sized?(嗯,是的,一个继承了 trait,另一个是参数绑定,但是从 Rust 的 trait 对象的角度来看?

  • 我必须做出的首选object_safety_dynamic改变是什么?

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;
}
4

2 回答 2

19

为什么这个特定的例子不起作用?特征对象一章 指出:

那么是什么让方法对象安全呢?每种方法都必须要求Self: Sized

这不是实现了吗?

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

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

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

应用操作的具体类型是未知的,这就是为什么要使用 trait 对象的原因,因为它允许以统一的方式将一组异构类型操作到汇编级别

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

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

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

Trait: Sized和有什么不一样Self: Sized?(嗯,是的,一个继承了 trait,另一个是参数绑定,但是从 Rust 的 trait 对象的角度来看?)

Rust 中没有继承。在这两种情况下,这些都是界限

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

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

我必须做出的首选改变是什么object_safety_dynamic

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

于 2017-05-21T13:19:05.190 回答
5

制作Trait一个超类型Sized并没有帮助——事实上,正如错误消息所说,这是不允许的。的每个实现Trait仍然会有不同的大小,所以你的函数object_safety_dynamic不能被编译。这里不能使用单态化,因为没有泛型参数,所以编译后的函数必须适用Trait.

但是,引用确实具有固定大小,因此将参数变为引用将起作用:

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

fn object_safety_dynamic(x: &Trait) {}

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

于 2017-05-21T11:31:30.913 回答