了解特征和对象安全

lef*_*ead 3 traits rust trait-objects

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

struct S {
    x: i32
}

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

fn object_safety_dynamic(x: Trait) {}
Run Code Online (Sandbox Code Playgroud)

我收到

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

当添加/更改:Sized为特征的继承或f绑定时,我收到稍微不同的错误消息.

有人能解释一下:

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

  • Trait: Sized和之间有什么区别where 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;
}
Run Code Online (Sandbox Code Playgroud)

Mat*_* M. 7

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

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

性状对象是在面向对象的范式的接口:

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

应用操作的具体类型具体是为什么使用特征对象的事实,因为它允许以统一的方式操作异构的类型集,直到汇编级.

然而,具体类型未知的事实意味着包含存储器的存储区域的大小也是未知的; 因此,Trait对象只能在引用指针之后操作,例如&TraitObject,&mut TraitObject或者Box<TraitObject>.

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

  • 指向虚拟表的指针,该虚拟表是一个结构,在固定偏移处的每个"方法"中保存一个函数指针,
  • 指向对象实际数据的指针.

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

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

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

注意:在实现特征时,所有方法必须最终具有定义; 因此,如果为带有Self: Sized绑定的方法提供默认实现,后者仅非常有用,如此处所示.

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

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


Pet*_*all 5

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

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

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

fn object_safety_dynamic(x: &Trait) {}
Run Code Online (Sandbox Code Playgroud)

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