I have recently seen code using the dyn keyword:
fn foo(arg: &dyn Display) {}
fn bar() -> Box<dyn Display> {}
What does this syntax mean?
TL;DR: It's a syntax for specifying the type of a trait object and must be specified for clarity reasons.
Since Rust 1.0, traits have led a double life. Once a trait has been declared, it can be used either as a trait or as a type:
// As a trait
impl MyTrait for SomeType {}
// As a type!
impl MyTrait {}
impl AnotherTrait for MyTrait {}
As you can imagine, this double meaning can cause some confusion. Additionally, since the MyTrait type is an unsized / dynamically-sized type, this can expose people to very complex error messages.
To ameliorate this problem, RFC 2113 introduced the dyn syntax. This syntax is available starting in Rust 1.27:
use std::{fmt::Display, sync::Arc};
fn main() {
let display_ref: &dyn Display = &42;
let display_box: Box<dyn Display> = Box::new(42);
let display_arc: Arc<dyn Display> = Arc::new(42);
}
This new keyword parallels the impl Trait syntax and strives to make the type of a trait object more obviously distinct from the "bare" trait syntax.
dyn is short for "dynamic" and refers to the fact that trait objects perform dynamic dispatch. This means that the decision of exactly which function is called will occur at program run time. Contrast this to static dispatch which uses the impl Trait syntax.
The syntax without dyn is now deprecated and it's likely that in a subsequent edition of Rust it will be removed.
Why would I implement methods on a trait instead of as part of the trait?
What makes something a "trait object"?
TLDR: "dyn" allows you to store in a Box a mix of Apples and Oranges, because they all implement the same trait of Fruit, which is what your Box is using as a type constraint, instead of just a generic type. This is because Generic allows any ONE of Apple OR Orange, but not both:
Vec<Box<T>> --> Vector can hold boxes of either Apples OR Oranges structs
Vec<Box<dyn Fruit>> --> Vector can now hold a mix of boxes of Apples AND Oranges Structs
If you want to store multiple types to the same instance of a data-structure, you have to use a trait wrapping a generic type and tag it as a "dyn", which will then cause that generic type to be resolved each time it's called, during runtime.
Sometimes, rather than using a type (String, &str, i32, etc...) or generic (T, Vec, etc...), we are using a trait as the type constraint (i.e. TryFrom). This is to allow us to store multiple types (all implementing the required trait), in the same data-structure instance (you will probably need to Box<> it too).
"dyn" basically tells the compiler that we don't know what the type is going to be at compile-time in place of the trait, and that it will be determined at run-time. This allows the final type to actually be a mixture of types that all implement the trait.
For generics, the compiler will hard-code the type in place of our generic type at the first use of the call to our data-structure consuming the generics. Every other call to store data in that same data-structure is expected to be using the same type as in the first call.
WARNING
As with all things, there is a performance penalty for implementing added flexibility, and this case definitely has a performance penalty.
I found this blog post to explain this feature really clearly: https://medium.com/digitalfrontiers/rust-dynamic-dispatching-deep-dive-236a5896e49b
Relevant excerpt:
struct Service<T:Backend>{
backend: Vec<T> // Either Vec<TypeA> or Vec<TypeB>, not both
}
...
let mut backends = Vec::new();
backends.push(TypeA);
backends.push(TypeB); // <---- Type error here
vs
struct Service{
backends: Vec<Box<dyn Backend>>
}
...
let mut backends = Vec::new();
backends.push( Box::new(PositiveBackend{}) as Box<dyn Backend>);
backends.push( Box::new(NegativeBackend{}) as Box<dyn Backend>);
The dyn keyword is used to indicate that a type is a trait object. According to the Rust docs:
A trait object is an opaque value of another type that implements a
set of traits.
In other words, we do not know the specific type of the object at compile time, we just know that the object implements the trait.
Because the size of a trait object is unknown at compile time they must be placed behind a pointer. For example, if Trait is your trait name then you can use your trait objects in the following manner:
Box<dyn Trait>
&dyn Trait
and other pointer types
The variables/parameters which hold the trait objects are fat pointers which consists of the following components:
pointer to the object in memory
pointer to that object’s vtable, a vtable is a table with pointers which point to the actual method(s) implementation(s).
See my answer on What makes something a “trait object”? for further details.
Related
I'm reading some code and it has a consume function which makes it possible for me to pass my own function f.
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
I wrote some similar code, but like this:
pub fn phy_receive(
&mut self,
f: &mut dyn FnMut(&[u8])
) -> u8 {
and to be fair I don't know what is the difference, aside from FnOnce vs FnMut. What is the difference between using dyn vs a generic type parameter to specify this function?
Using dyn with types results in dynamic dispatch (hence the dyn keyword), whereas using a (constrained) generic parameter results in monomorphism.
General explanation
Dynamic dispatch
Dynamic dispatch means that a method call is resolved at runtime. It is generally more expensive in terms of runtime resources than monomorphism.
For example, say you have the following trait
trait MyTrait {
fn f(&self);
fn g(&self);
}
and a struct MyStruct which implements that trait. If you use a dyn reference to that trait (e.g. &dyn MyTrait), and pass a reference to a MyStruct object to it, what happens is the following:
A "vtable" data structure is created. This is a table containing pointers to the MyStruct implementations of f and g.
A pointer to this vtable is stored with the &dyn MyTrait reference, hence the reference will be twice its usual size; sometimes &dyn references are called "fat references" for this reason.
Calling f and g will then result in indirect function calls using the pointers stored in the vtable.
Monomorphism
Monomorphism means that the code is generated at compile-time. It's similar to copy and paste. Using MyTrait and MyStruct defined in the previous section, imagine you have a function like the following:
fn sample<T: MyTrait>(t: T) { ... }
And you pass a MyStruct to it:
sample(MyStruct);
What happens is the following:
During compile time, a copy of the sample function is created specifically for the MyStruct type. In very simple terms, this is as if you copied and pasted the sample function definition and replaced T with MyStruct:
fn sample__MyStruct(t: MyStruct) { ... }
The sample(MyStruct) call gets compiled into sample__MyStruct(MyStruct).
This means that in general, monomorphism can be more expensive in terms of binary code size (since you are essentially duplicating similar chunks of code, but for different types), but there's no runtime cost like there is with dynamic dispatch.
Monomorphism is also generally more expensive in terms of compile times: because it essentially does copy-paste of code, codebases that use monomorphism abundantly tend to compile a bit slower.
Your example
Since FnMut is just a trait, the above discussion applies directly to your question. Here's the trait definition:
pub trait FnMut<Args>: FnOnce<Args> {
pub extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
Disregarding the extern "rust-call" weirdness, this is a trait just like MyTrait above. This trait is implemented by certain Rust functions, so any of those functions is analogous to MyStruct from above. Using &dyn FnMut<...> will result in dynamic dispatch, and using <T: FnMut<...>> will result in monomorphism.
My 2 cents and general advice
Certain situations will require you to use a dynamic dispatch. For example, if you have a Vec of external objects implementing a certain trait, you have no choice but to use dynamic dispatch. For example,
Vec<Box<dyn Debug>>. If those objects are internal to your code, though, you could use an enum type and monomorphism.
If your trait contains an associated type or a generic method, you will have to use monomorphism, because such traits are not object safe.
Everything else being equal, my advice is to pick one preference and stick with it in your codebase. From what I've seen, most people tend to prefer defaulting to generics and monomorphism.
I can see the difference between dyn and (static) impl Traits in return position, such as:
fn foo() -> Box<dyn Trait> {}
vs
fn foo() -> impl Trait {}
Where in the dyn version I'm allowed to return different types as long they all implement the Trait, while in the impl version I'm only allowed to return the same type (same applies if I return a reference).
But I can't see the purpose of a dyn Trait in argument position such as:
fn foo(x: &dyn Trait) {}
vs
fn foo(x: &impl Trait) {} // syntatic sugar of `fn foo<T: Trait>(x: &T){}`
What is the difference between the two? Why would I use one or the other? And what does the dyn version allow me to do that the static one doesn't (that I cannot do for example by relaxing the implicit Sized restriction with ?Sized)?
If you're familiar with C++/Java, then dyn corresponds to "interface reference", so it implies dynamic polymorphism (thus requires a bunch of jumps over references, so it's a bit slower).
impl being a syntatic sugar, defines a template for functions, thus every time you use the function with another type, you'll get a separate copy of function compiled specifically for this type. So no extra jumping, but your executable bloats with these copies. Rust's ideology tells to create templates with <T>, impl, unless there're too many versions compiled that the executable is too bloated.
I have this code (playground):
trait NodeLike: Sized {}
fn main() {
let s: Box<NodeLike> = panic!();
}
Which does not compile:
error[E0038]: the trait `NodeLike` cannot be made into an object
--> src/main.rs:4:12
|
4 | let s: Box<NodeLike> = panic!();
| ^^^^^^^^^^^^^ the trait `NodeLike` cannot be made into an object
|
= note: the trait cannot require that `Self : Sized`
After all I read, I still don't understand why it does not compile and why it does without the Sized constraint.
As far as I know:
Box<NodeLike> is treated as Box<dyn NodeLike> which uses dynamic dispatch for method calls.
Box<NodeLike> is sized anyways, regardless of its item type.
The sized/unsized theory is necessary because there are types whose size is not known upfront (like arrays or strings).
The Sized marker on traits enforces implementing types to be sized.
What does requiring that implementing types are Sized have to do with not being able to have objects (with dynamic dispatch) of that trait?
Having Self: ?Sized on the trait type itself is a required property for a trait object, i.e. for 'object safety', even though you can have an impl on a Self: ?Sized trait with a Sized type. Hence confusion.
It's a drawback that was decided upon in RFC 255 which deals with object safety (warning: obsolete Rust syntax).
It's a long read, but one of the alternatives was to determine 'object safety' by only analyzing the methods of the trait. It is admitted in the RFC that having this restriction will make some code that could have worked not to compile. ("This is a breaking change and forbids some safe code which is legal today.").
We can go around this if we lower the restriction only to the trait members function that actually need it, e.g. this compiles:
trait NodeLike {
fn method_foo(&self) -> Self
where
Self: Sized;
}
fn main() {
let s: Box<NodeLike> = panic!();
// Compiles!
}
However, we cannot call those Self: Sized methods via a trait object, and this is a limitation that is explained elsewhere. Here, calling s.method_foo(); will break compilation.
Note that the Self: Sized constraint limits compilation even if the method does not make use of Self at all and could have been a callable trait object method otherwise.
pub trait AllValues {
fn all_values() -> Vec<Self> where Self: std::marker::Sized;
}
use rand::Rand;
use rand::Rng;
impl<T: AllValues + Sized> Rand for T {
fn rand<R: Rng, T>(rng: &mut R) -> T {
let values = T::all_values();
let len = values.len();
if len == 0 {
panic!("Cannot pick a random value because T::all_values() returned an empty vector!")
} else {
let i = rng.gen_range(0, len);
values[i]
}
}
}
The preceding code produces the following compile-time error:
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`); only traits defined in the current crate can be implemented for a type parameter
--> src/lib.rs:137:1
|
137 | impl<T: AllValues + Sized> Rand for T {
| ^
According to the restrictions on implementing traits mentioned here I should be able to implement Rand for AllValues since AllValues is defined in my crate. Is this actually allowed by the coherence/orphan impls rules? And if so, what is the right way to implement Rand for things that implement AllValues?
I should be able to implement Rand for AllValues since AllValues is defined in my crate.
No, you are only allowed to implement your own trait AllValues for types you didn't define. You can't make the logical jump to implementing an unrelated trait that you also didn't define.
There are two considerations to remember:
If your trait is public (which it is based on the code you've provided), you aren't the only one that can implement the trait. Consumers of your crate might be able to implement it for their own types, where they might also decide to implement Rand!
The rand crate might decide to implement Rand for T some time in the future.
What is the right way to implement Rand for things that implement AllValues?
I don't believe there is one. I'd just introduce a wrapper type that holds a value or a reference to a value that implements your trait and implement Rand for that.
See also:
How do I implement a trait I don't own for a type I don't own?
I see now where my mistake in interpretation was. Quoting from the the traits section of the book:
There’s one more restriction on implementing traits: either the trait
or the type you’re implementing it for must be defined by you. Or more
precisely, one of them must be defined in the same crate as the impl
you're writing.
(Emphasis added.)
Since I was trying to implement a trait I must have read that as "either the trait or the trait you’re implementing it for". This discussion about an eventually implemented rfc specifically mentions a similar case to the one I presented : impl<T: Copy> Clone for T as something that would not be allowed.
Creating a wrapper type as suggested elsewhere is one solution for this problem. Assuming the ownership of the type implementations allows it, implementing the trait explicitly for each concrete instance, (optionally condensing the code with a macro,) as suggested here is another.
I am trying to get a dynamically dispatchable borrow to an instance of an object implementing both Reader and Seek.
I understand that Rust can do dynamic dispatch as long as there is only one trait involved.
use std::io::{Read, Seek};
fn user(stream: &mut Read) {}
With two or more trait bounds though, I am forced to use a type parameter:
fn user_gen<T: Read + Seek>(stream: &mut T) {}
As the actual type underneath is a builder, it would have to store the borrowed object in some way, and using a type parameter for this would make the implementation more complex (I have three type parameters already).
Ideally, I would be able to do something like that:
fn user_dynamic(stream: &mut (Read + Seek)) {}
This does not compile:
error[E0225]: only auto traits can be used as additional traits in a trait object
--> src/main.rs:3:38
|
3 | fn user_dynamic(stream: &mut (Read + Seek)) {}
| ^^^^ non-auto additional trait
I understand that dynamic dispatch is done through fat pointers, and usually these only refer to one method table, not to multiple ones. I have not seen a statically compiled language that would support this, but such a feature would help me a lot.
You can create an empty trait that merges those two traits:
use std::io::{Read, Seek};
trait SeekRead: Seek + Read {}
impl<T: Seek + Read> SeekRead for T {}
fn user_dynamic(stream: &mut SeekRead) {}
This will create a new vtable for SeekRead that contains all the function pointers of both Seek and Read.
You will not be able to cast your &mut SeekRead to either &mut Seek or &mut Read without some trickery (see Why doesn't Rust support trait object upcasting?)