为什么使用盒装对象而不是特征对象?

Mar*_*cus 3 traits rust trait-objects

在《Rust for Rustaceans》一书中,作者写道:

\n
\n

但从广义上讲,您\xe2\x80\x99 将希望在库中使用静态调度,在二进制文件中使用动态调度。在图书馆中,您希望允许用户决定哪种调度最适合他们,因为您不知道他们的需求是什么。

\n
\n

我猜想,在二进制情况下,他指的是:

\n
fn flexible_dispatch_method(_: &dyn MyTrait) {}\n\n// static dispatch\n//\nlet obj = MyType {};\nflexible_dispatch_method(&obj);\n\n// dynamic dispatch\n//\nlet trait_obj: &dyn MyTrait = &MyType {};\nflexible_dispatch_method(trait_obj);\n
Run Code Online (Sandbox Code Playgroud)\n

鉴于上述情况,使用装箱对象而不是特征对象有什么优势?是不是因为需要使用生命周期:

\n
fn return_new_trait_object<\'a>() -> &\'a dyn MyTrait {\n    &MyType {}\n}\n
Run Code Online (Sandbox Code Playgroud)\n

或者还有别的什么?根据我的理解,在动态调度情况下,无论如何,对象需要在堆中分配,所以我认为与装箱对象没有太大区别。

\n

Ian*_* S. 13

我认为您可能在这里误解了几件事。

  • (通常)每当您调用非 .具体类型的方法时,就会发生静态分派dyn Trait。这是编译器决定在编译时调用哪个函数的时候。
  • 特征对象是任何指向 a 的指针dyn Trait,包括Box<dyn Trait>&dyn TraitArc<dyn Trait>等。当您通过 a 调用函数时dyn Trait,编译器会插入代码来查找要在运行时调用的函数,从而实现多态性和更大的灵活性。

在您的代码中,flexible_dispatch_method(_: &dyn MyTrait)始终使用动态分派,正如其参数具有类型这一事实所表明的那样&dyn MyTrait。静态调度的一个示例如下:

fn flexible_dispatch_method<T: MyTrait + ?Sized>(_: &T) {}
Run Code Online (Sandbox Code Playgroud)

通过此声明,您的第一次使用将使用静态分派,第二次使用将使用动态分派。

动态调度更加灵活,因为它避免了到处都有泛型。这对于编写您可能想要具有多态性并轻松添加新实现的大型应用程序非常有用。然而,动态调度会带来性能成本,因此库应尽可能将调度选择权留给调用者。

至于何时使用&dyn Traitvs Box<dyn Trait>:这完全取决于所有权。如果您想要一个拥有的特征对象Box<dyn Trait>,请使用,如果您想要一个借用的特征对象,请使用&dyn Trait

  • 我猜你正在谈论 Jon Gjengset 的书(至少书名相同)?我记得他提到这本书主要针对那些已经了解 Rust 并想要更深入了解的人。这并不是说你不应该阅读它,我非常尊重 Jon 的工作,我确信它很棒,在阅读 [Rust 书](https: //doc.rust-lang.org/book/) 首先更熟悉该语言。如果您遇到困难,Rust 社区随时为您提供帮助! (3认同)