什么是'哪里'特质中的条款吗?

时间:2017-05-21 14:43:27

标签: syntax rust traits type-bounds

如果我有这段代码:

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

    fn g(&self) -> i32;
}


fn object_safety_dynamic(x: &Trait) {
    x.f();    // error 
    x.g();    // works
}

where条款实际上做了什么?

天真地,我在想where Self: Sized;指示实施Trait的类型,例如'如果您为类型Trait实施A类型{{1}必须是大小的,即它可以是A但不是i32

然而,这样的约束更像是[i32](如果我错了,请纠正我)?

现在我注意到trait Trait: Sized确实可以确定我是否可以在where Self: Sized;内拨打fg

我的问题:

  1. 幕后会发生什么?

  2. 我用object_safety_dynamic告诉编译器是什么(用简单的英语)让where Self: Sized;工作但g()没有?

  3. 特别是:由于f()无论如何都是参考,因此对于各种(大小或未大小)类型,&selff之间存在编译差异。它不会g,而不管_vtable_f_or_g(*self) -> i32或类型是否大小?

  4. 为什么我可以为whereu8实施特征。难道编译器真的不能阻止我为[u8]实施f(),而不是在呼叫站点抛出错误吗?

2 个答案:

答案 0 :(得分:3)

  

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

这表示f仅针对同时实现Sized的类型定义。未归档的类型仍可以实现Trait,但f将无法使用。

object_safety_dynamic内,调用x.f()实际上正在执行:(*x).f()。虽然x的大小是因为它是指针,但*x可能不是因为它可能是 Trait的任何实现。但是函数内部的代码必须适用于任何有效参数,因此不允许在那里调用x.f()

答案 1 :(得分:3)

  

where子句实际上做了什么?

     

天真地,我在想自我:大小;指示实现Trait的类型,例如'如果你为A类实现Trait,你的类型A必须是大小的,即它可以是i32而不是[i32]。

     

然而,这样的约束宁愿作为特质Trait:Sized

这是正确的。

但是,在这种情况下,绑定仅适用于该函数。仅在呼叫站点检查函数的where边界。

  

幕后会发生什么?

关于rust的语法有一个令人困惑的地方,Trait可以指代

  • 特质Trait;或
  • "特质对象" Trait,实际上是一种类型,而不是一种对象。

Sized是一种特征,T的任何类型Sized都可以将其大小视为std::mem::size_of::<T>()的常量。这些未调整大小的类型为str[u8],其内容的大小不固定。

类型Trait也是未标注的。直观地说,这是因为作为类型的Trait由实现特征Trait的所有类型值组成,其可能具有不同的大小。这意味着你永远不会有Trait类型的值 - 你只能通过&#34;胖指针&#34;来引用一个值。例如&TraitBox<Trait>等。它们的大小为2个指针 - 一个用于vtable,一个用于数据。看起来大致如下:

struct &Trait {
    pub data: *mut (),
    pub vtable: *mut (),
}

表格会自动显示:

impl Trait /* the trait */ for Trait /* the type */ {
    fn f(&self) -> i32 where Self: Sized { .. }
    fn g(&self) -> i32 {
        /* vtable magic: something like (self.vtable.g)(self.data) */
    }
}
  

什么(用简单的英语)我实际上告诉编译器Self:Sized;这使得g()工作但f()没有?

请注意,正如我所提到的,Trait不是Sized,因此不满足绑定Self: Sized,因此无法在{{1}处调用函数f }}

  

特别是:因为&amp; self无论如何都是参考,对于各种(大小或未大小)类型,f和g之间存在编译差异。它不会总是归结为像_vtable_f_or_g(* self) - &gt;这样的东西。 i32,无论在何处或类型是否大小?

类型Self == Trait 总是未确定。哪种类型被强制转移到Trait并不重要。使用Trait变量调用函数的方法是直接使用它:

Sized
  

为什么我可以为u8和[u8]实现Trait。难道编译器实际上不能阻止我为[u8]实现f(),而不是在调用站点抛出错误吗?

因为特征不受fn generic<T: Trait + Sized>(x: &T) { // the `Sized` bound is implicit, added here for clarity x.f(); // compiles just fine x.g(); } 限制 - 函数Self: Sized是。{所以没有什么可以阻止你实现这个功能 - 只是因为函数的界限永远不会被满足,所以你永远不能称之为。