当用作方法参数时,&Trait和impl Trait有什么区别?

Qui*_*ana 6 syntax reference traits rust

到目前为止,在我的项目中,我使用许多特性来允许在注入依赖项的单元测试中进行模拟/存根.然而,到目前为止我所做的一个细节似乎很可疑,我甚至惊讶它甚至编译.我担心会发生一些我看不见或不明白的危险事件.它基于这两种方法签名之间的差异:

fn confirm<T>(subject: &MyTrait<T>) ...
fn confirm<T>(subject: impl MyTrait<T>) ...
Run Code Online (Sandbox Code Playgroud)

我只是impl ...在方法参数中发现了语法,它似乎是唯一记录的方法,但是我的测试已经使用了另一种方式,我根据Go解决了同样的问题(方法的大小)来直觉编译时的参数,当参数可以是接口的任何实现者,并且引用可以来拯救).

这两者有什么区别?为什么他们都被允许?它们都代表合法用例,还是我的参考语法(&MyTrait<T>)严格来说更糟糕?

Sve*_*ach 9

两者不同,用途不同.两者都很有用,根据情况,一个或另一个可能是最好的选择.

第一种情况,&MyTrait<T>最好是&dyn MyTrait<T>用现代Rust 写的.这是一个所谓的特质对象.该引用指向任何类型的实现MyTrait<T>,并且在运行时动态调度方法调用.为了实现这一点,引用实际上是一个胖指针; 除了指向对象的指针外,它还存储指向对象类型的虚方法表的指针,以允许动态调度.如果对象的实际类型仅在运行时已知,则这是您可以使用的唯一版本,因为在这种情况下您需要使用动态分派.该方法的缺点是存在运行时成本,并且它仅适用于对象安全的特征.

第二种情况,impl MyTrait<T>表示MyTrait<T>再次实现的任何类型,但在这种情况下,需要在编译时知道确切类型.原型

fn confirm<T>(subject: impl MyTrait<T>);
Run Code Online (Sandbox Code Playgroud)

相当于

fn confirm<M, T>(subject: M)
where
    M: MyTrait<T>;
Run Code Online (Sandbox Code Playgroud)

对于M代码中使用的每种类型,编译器confim在二进制文件中创建单独的版本,并在编译时静态调度方法调用.如果在编译时已知所有类型,则此版本是首选,因为您不需要支付动态分派到具体类型的运行时成本.

两个原型之间的另一个区别是第一个版本subject通过引用接受,而第二个版本使用传入的参数.但这不是概念上的区别 - 虽然第一个版本无法编写来使用对象,第二个版本可以subject通过引用轻松编写接受:

fn confirm<T>(subject: &impl MyTrait<T>);
Run Code Online (Sandbox Code Playgroud)

鉴于您介绍了促进测试的特性,您可能更喜欢&impl MyTrait<T>.

  • 评论只是链接到[什么使某些东西成为特质对象?](/sf/ask/1929749461/) (3认同)