了解特征和对象安全

时间:2017-05-21 11:18:30

标签: rust traits trait-objects

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

struct S {
    x: i32
}

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

fn object_safety_dynamic(x: Trait) {}

我收到了

fn object_safety_dynamic(x: Trait) {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `traits::Trait` cannot be made into an object
= note: the trait cannot require that `Self : Sized`

当添加/更改:Sized作为特征的继承或f的界限时,我会收到略有不同的错误消息。

有人可以解释一下:

  • 为什么这个特定的例子不起作用?章节Trait Objects陈述“那么什么使方法对象安全?每种方法都必须要求Self: Sized。是不是满足了?

  • Trait: Sizedwhere Self: Sized之间有什么区别? (嗯,是的,一个继承了特性,另一个是参数绑定,但是从Rust的特征对象的角度来看?

  • 我必须让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;
}

2 个答案:

答案 0 :(得分:7)

  

为什么这个特定的例子不起作用? Trait Objects一章说明“那么什么使方法对象安全?每种方法都必须要求Self:Sized”。这不是完成了吗?

这个问题确实是:什么是特质对象

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

  • 它暴露了一组有限的方法,
  • 适用于未知的混凝土类型。

应用操作的具体类型未知的事实是具体使用特征对象的原因,因为它允许以统一的方式操作异构的一组类型直到汇编级别

然而,具体类型未知的事实意味着包含内存的内存区域的大小未知;因此,Trait对象只能在引用指针后面操作,例如&TraitObject&mut TraitObjectBox<TraitObject>。< / p>

在内存级别,每个都表示相同:

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

Trait:Sized和Self:Sized之间有什么区别? (嗯,是的,一个继承了特征,另一个是参数绑定,但是从Rust的特征对象的角度来看?)

Rust中没有继承。在两个案例中, bounds

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

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

  

我必须让object_safety_dynamic工作的首选更改是什么?

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

答案 1 :(得分:3)

使<base href="/">成为Trait的超类型并没有帮助 - 事实上它是不允许的,正如错误消息所示。 Sized的每个实现仍然具有不同的大小,因此无法编译函数Trait。这里不能使用单态化,因为没有泛型参数,因此编译的函数必须适用于 object_safety_dynamic的所有实现。

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

Trait

特质对象总是某种类型的引用,例如trait Trait { fn f(&self) -> i32; } fn object_safety_dynamic(x: &Trait) {} Box<T>。这正是因为特征的实现大小不同,而引用类型具有已知的固定大小。