什么使某事成为"特质对象"?

She*_*ter 38 traits rust

最近的Rust改变使得"特质对象"对我来说更加突出,但我只是模糊地掌握了什么让某些东西成为特质对象.特别是一个变化是允许特征对象将特征实现转发到内部类型的即将发生的变化.

鉴于一个特点Foo,我很确定这Box<Foo>是一个特质对象.是否&Foo也是一个特质对象?那么其他智能指针之类的东西Rc还是Arc?我怎样才能创建自己的类型作为特征对象呢?

引用只提到一次trait对象,但没有提到定义.

Pao*_*lla 54

当您有指向特征的指针时,您有特征对象. Box,Arc,Rc和基准&都在其核心的指针.在定义"特征对象"方面,它们以相同的方式工作.

"特质对象"是Rust对动态调度的看法.这是一个我希望有助于显示特征对象的示例:

// define an example struct, make it printable
#[derive(Debug)]
struct Foo;

// an example trait
trait Bar {
    fn baz(&self);
}

// implement the trait for Foo
impl Bar for Foo {
    fn baz(&self) { println!("{:?}", self) }
}

// This is a generic function that takes any T that implements trait Bar.
// It must resolve to a specific concrete T at compile time.
// The compiler creates a different version of this function
// for each concrete type used to call it so &T here is NOT
// a trait object (as T will represent a known, sized type
// after compilation)
fn static_dispatch<T>(t: &T) where T:Bar {
    t.baz(); // we can do this because t implements Bar
}

// This function takes a pointer to a something that implements trait Bar
// (it'll know what it is only at runtime). &dyn Bar is a trait object.
// There's only one version of this function at runtime, so this
// reduces the size of the compiled program if the function
// is called with several different types vs using static_dispatch.
// However performance is slightly lower, as the &dyn Bar that 
// dynamic_dispatch receives is a pointer to the object +
// a vtable with all the Bar methods that the object implements.
// Calling baz() on t means having to look it up in this vtable.
fn dynamic_dispatch(t: &dyn Bar) {
    // ----------------^
    // this is the trait object! It would also work with Box<dyn Bar> or 
    // Rc<dyn Bar> or Arc<dyn Bar>
    //
    t.baz(); // we can do this because t implements Bar
}

fn main() {
    let foo = Foo;
    static_dispatch(&foo);
    dynamic_dispatch(&foo);
}
Run Code Online (Sandbox Code Playgroud)

为了进一步参考,Rust书中有一个很好的Trait Objects章节

  • @Shepmaster,类型不像特征对象那样"行动"; 更确切地说,任何指向特征*的*指针都是特征对象,并且可以有不同类型的指针.`Box <T>`是一个拥有指针,`Rc <T>`是一个共享的所有权指针,`Arc <T>`是一个多线程的共享所有权指针,等等.原则上,每个都可以用来定义特征对象,但目前只有引用和`Box`对此有用.所以不,现在你不能创建可用于创建特征对象的自定义指针类型. (6认同)
  • @Shepmaster,不,这并不完全正确。`Box&lt;Trait&gt;`/可能的 `Rc&lt;Trait&gt;` *也是*特征对象本身,并且它们不会转换或提供 `&amp;Trait`。 (2认同)
  • &gt; 术语“特质对象”可以应用于两者,并且通常不会引起混淆。FWIW,我对此感到很困惑:)这种不明确的用法感觉就像特质对象 = 指向数据的胖指针 + vtable,但这些胖指针同时也以某种方式被认为是未调整大小的,这并不会使感觉。幸运的是,[Rust 参考文献目前对此很清楚](https://doc.rust-lang.org/reference/types/trait-object.html):未调整大小的值“dyn Trait”本身是一个特征对象,并且必须在某种类型的指针后面使用(`&amp;dyn Trait`、`Box&lt;dyn Trait&gt;` 等)。 (2认同)

Wil*_*een 7

Trait 对象是动态调度的 Rust 实现。动态分派允许在运行时选择多态操作(特征方法)的一种特定实现。动态调度允许非常灵活的架构,因为我们可以在运行时交换函数实现。然而,与动态调度相关的运行时成本很小。

\n

保存特征对象的变量/参数是胖指针,由以下组件组成:

\n
    \n
  • 指向内存中对象的指针
  • \n
  • 指向该对象\xe2\x80\x99s vtable 的指针,vtable 是一个带有指向实际方法实现的指针的表。
  • \n
\n

例子

\n
struct Point {\n    x: i64,\n    y: i64,\n    z: i64,\n}\n\ntrait Print {\n    fn print(&self);\n}\n\n// dyn Print is actually a type and we can implement methods on it\nimpl dyn Print + 'static {\n    fn print_traitobject(&self) {\n        println!("from trait object");\n    }\n}\n\nimpl Print for Point {\n    fn print(&self) {\n        println!("x: {}, y: {}, z: {}", self.x, self.y, self.z);\n    }\n}\n\n// static dispatch (compile time): compiler must know specific versions\n// at compile time generates a version for each type\n\n// compiler will use monomorphization to create different versions of the function\n// for each type. However, because they can be inlined, it generally has a faster runtime\n// compared to dynamic dispatch\nfn static_dispatch<T: Print>(point: &T) {\n    point.print();\n}\n\n// dynamic dispatch (run time): compiler doesn't need to know specific versions\n// at compile time because it will use a pointer to the data and the vtable.\n// The vtable contains pointers to all the different different function implementations.\n// Because it has to do lookups at runtime it is generally slower compared to static dispatch\n\n// point_trait_obj is a trait object\nfn dynamic_dispatch(point_trait_obj: &(dyn Print + 'static)) {\n    point_trait_obj.print();\n    point_trait_obj.print_traitobject();\n}\n\nfn main() {\n    let point = Point { x: 1, y: 2, z: 3 };\n\n    // On the next line the compiler knows that the generic type T is Point\n    static_dispatch(&point);\n\n    // This function takes any obj which implements Print trait\n    // We could, at runtime, change the specfic type as long as it implements the Print trait\n    dynamic_dispatch(&point);\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Vin*_*tel 6

简短回答:您只能将对象安全的特征变成特征对象。

对象安全特征:不解析为具体实现类型的特征。在实践中,有两个规则控制着一个 trait 是否是对象安全的。

  1. 返回类型不是 Self。
  2. 没有泛型类型参数。

任何满足这两条规则的特征都可以用作特征对象。

对象安全的特征示例可以用作特征对象

trait Draw {
    fn draw(&self);
}
Run Code Online (Sandbox Code Playgroud)

不能用作特征对象的特征示例:

trait Draw {
    fn draw(&self) -> Self;
}
Run Code Online (Sandbox Code Playgroud)

详细解释:https : //doc.rust-lang.org/book/second-edition/ch17-02-trait-objects.html