简体   繁体   English

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

[英]What is the difference between &Trait and impl Trait when used as method arguments?

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>) ...

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

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? 它们都代表合法用例,还是我的参考语法( &MyTrait<T> )严格来说更糟糕?

The two are different, and serve different purposes. 两者不同,用途不同。 Both are useful, and depending on circumstances one or the other may be the best choice. 两者都很有用,根据情况,一个或另一个可能是最好的选择。

The first case, &MyTrait<T> , is preferably written &dyn MyTrait<T> in modern Rust. 第一种情况, &MyTrait<T> ,最好在现代Rust中编写&dyn MyTrait<T> It is a so-called trait object . 这是一个所谓的特质对象 The reference points to any type implementing MyTrait<T> , and method calls are dispatched dynamically at runtime. 该引用指向实现MyTrait<T>任何类型,并且在运行时动态调度方法调用。 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 . 该方法的缺点是存在运行时成本,并且它仅适用于对象安全的特征

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

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

is equivalent to 相当于

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

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

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

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

Given that you introduced the traits to facilitate testing, it is likely that you should prefer &impl MyTrait<T> . 鉴于您引入了特性以便于测试,您可能更喜欢&impl MyTrait<T>

It is indeed different. 确实不同。 The impl version is equivalent to the following: impl版本等同于以下内容:

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

so unlike the first version, subject is moved (passed by value) into confirm , rather than passed by reference. 与第一个版本不同, subject被移动(通过值传递)到confirm ,而不是通过引用传递。 So in the impl version, confirm takes ownership of this value. 因此,在impl版本中, confirm将获得此值的所有权。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 &#39;/&#39; 和 &#39;//&#39; 用于除法时有什么区别? - What is the difference between '/' and '//' when used for division? Scala 中大括号和圆括号之间的正式区别是什么,何时应该使用它们? - What is the formal difference in Scala between braces and parentheses, and when should they be used? 特征中的“ where”子句有什么作用? - What does the 'where' clause within a trait do? 如何使用参数定义特征并返回类似Fn的类型? - How can I define a trait with arguments and return types like Fn? 带有默认值和关键字参数的参数之间有什么区别? - What's the difference between arguments with default values and keyword-arguments? 特性中的静态函数可以调用相同特性中的另一个静态函数吗? - Can a static function in a trait call another static function in same trait? 为实现特定类型的 trait 的泛型类型实现 trait - Implement a trait for generic types that implement a trait for a specific type 是否在方法定义中省略“ self”有什么区别? - What's the difference between omitting `self` in method definition or not? 特定方法和一般类中的“使用类型”语法有什么区别? - what is difference between “using type” syntax in specific method and in general class? 将方法作为数组引用与字符串之间的区别是什么? - What's the difference between referencing a method as an array versus a string?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM