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
It is indeed different. The impl
version is equivalent to the following:
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. So in the impl
version, confirm
takes ownership of this value.
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. 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.
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>);
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. 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:
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>
.