Syntax &;之间的区别是什么;Trait和impl Trait何时用作方法参数?

Syntax &;之间的区别是什么;Trait和impl Trait何时用作方法参数?,syntax,reference,rust,traits,Syntax,Reference,Rust,Traits,到目前为止,在我的项目中,我使用了许多特性来允许对注入的依赖项进行单元测试中的模拟/存根。然而,到目前为止,我正在做的一个细节似乎是如此可疑,以至于我很惊讶它竟然能编译出来。我担心正在发生一些我看不见也不明白的危险事情。它基于这两种方法签名之间的差异: fn confirm<T>(subject: &MyTrait<T>) ... fn confirm<T>(subject: impl MyTrait<T>) ... fn确认(主题:&M

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

fn confirm<T>(subject: &MyTrait<T>) ...
fn confirm<T>(subject: impl MyTrait<T>) ...
fn确认(主题:&MyTrait)。。。
fn确认(受试者:impl MyTrait)。。。
我只是刚刚在方法参数中发现了
impl…
语法,这似乎是唯一有文档记录的方法,但我的测试已经通过了使用另一种方法,这是基于Go如何解决相同问题的直觉得出的(编译时方法参数的大小,此时参数可以是接口的任何实现者,并且可以使用引用)


这两者之间的区别是什么?为什么它们都被允许?它们都代表合法的用例,还是我的引用语法(
&MyTrait
)严格来说是一个更糟糕的想法?

确实不同。
impl
版本等同于以下内容:

fn confirm<T, M: MyTrait<T>>(subject: M) ...
fn确认(主题:M)。。。

因此,与第一个版本不同,
subject
被移动(按值传递)进入
confirm
,而不是通过引用传递。因此在
impl
版本中,
confirm
拥有此值。

两者不同,用途不同。两者都很有用,根据具体情况,其中一种可能是最佳选择

第一种情况,
&MyTrait
,最好是在现代Rust中编写的
&dyn MyTrait
。它是一个所谓的trait对象。引用指向实现
MyTrait
的任何类型,方法调用在运行时动态调度。为了实现这一点,引用实际上是一个胖指针;除了指向该对象还存储指向该对象类型的虚拟方法表的指针,以允许动态调度。如果对象的实际类型仅在运行时才为人所知,则这是唯一可以使用的版本,因为在这种情况下需要使用动态调度。该方法的缺点是存在运行时成本,并且只适用于那些有吸引力的特质

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

fn confirm<T>(subject: impl MyTrait<T>);

考虑到引入traits是为了方便测试,您可能更喜欢
&impl MyTrait

&MyTrait
是编写
&dyn MyTrait
的老方法。在这种情况下,我假定编译器(现在在编译时知道类型)只需链接到@Sven Marnach即可查看注释对任何大小的实现者都很满意。但是在这种情况下,除了性能/堆栈复制原因外,是否需要使用
&impl…
而不仅仅是
impl
?例如,假设我为
()
实现了
MyTrait
?(我在测试中做了类似的事情,结果是大量的
&()
被抛来抛去,这导致了我最初的怀疑).@Quintana如果类型较小且
Copy
,则没有理由使用引用。如果类型不是
Copy
,则按值传递对象将消耗它,因此您无法再使用它–它将被移动到函数中并在函数结尾处删除。谢谢,这是一个有用的信息(+1)但是,由于@Sven Marnach的答案更具涵盖性,我会接受这个答案。
fn confirm<M, T>(subject: M)
where
    M: MyTrait<T>;
fn confirm<T>(subject: &impl MyTrait<T>);