&Trait 和 impl Trait 在用作方法参数时有什么区别? [英] What is the difference between &Trait and impl Trait when used as method arguments?

查看:61
本文介绍了&Trait 和 impl Trait 在用作方法参数时有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In my project so far, I use many traits to permit mocking/stubbing in unit tests for injected dependencies. However, one detail of what I'm doing so far seems so suspicious that I'm surprised it even compiles. I'm worried that something dangerous is going on that I don't see or understand. It's based on the difference between these two method signatures:

fn confirm<T>(subject: &MyTrait<T>) ...
fn confirm<T>(subject: impl MyTrait<T>) ...

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

I only just discovered the impl ... syntax in method arguments, and it seems like the only documented way to do this, but my tests pass using the other way already, which I came to by intuition based on how Go solves the same problem (size of method argument at compile time, when argument can be any implementer of an interface, and references can come to the rescue).

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

What is the difference between these two? And why are they both allowed? Do they both represent legitimate use cases, or is my reference syntax (&MyTrait<T>) strictly a worse idea?

推荐答案

两者是不同的,用于不同的目的.两者都很有用,而且视情况而定,其中一个可能是最佳选择.

The two are different, and serve different purposes. Both are useful, and depending on circumstances one or the other may be the best choice.

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

The first case, &MyTrait<T>, is preferably written &dyn MyTrait<T> in modern Rust. It is a so-called trait object. The reference points to any type implementing MyTrait<T>, and method calls are dispatched dynamically at runtime. To make this possible, the reference is actually a fat pointer; apart from a pointer to the object it also stores a pointer to the virtual method table of the type of the object, to allow dynamic dispatch. If the actual type of your object only becomes known at runtime, this is the only version you can use, since you need to use dynamic dispatch in that case. The downside of the approach is that there is a runtime cost, and that it only works for traits that are object-safe.

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

The second case, impl MyTrait<T>, denotes any type implementing MyTrait<T> again, but in this case the exact type needs to be known at compile time. The prototype

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

相当于

fn confirm<M, T>(subject: M)
where
    M: MyTrait<T>;

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

For each type M that is used in your code, the compiler creates a separate version of confim in the binary, and method calls are dispatched statically at compile time. This version is preferable if all types are known at compile time, since you don't need to pay the runtime cost of dynamically dispatching to the concrete types.

两个原型之间的另一个区别是第一个版本通过引用接受 subject,而第二个版本使用传入的参数.不过,这不是概念上的区别——而不能编写第一个版本来使用对象,第二个版本可以很容易地编写为通过引用接受subject:

Another difference between the two prototypes is that the first version accepts subject by reference, while the second version consumes the argument that is passed in. This isn't a conceptual difference, though – while the first version cannot be written to consume the object, the second version can easily be written to accept subject by reference:

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

鉴于您引入了便于测试的特征,您很可能应该更喜欢 &impl MyTrait.

Given that you introduced the traits to facilitate testing, it is likely that you should prefer &impl MyTrait<T>.

这篇关于&amp;Trait 和 impl Trait 在用作方法参数时有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆