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 paramater 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.
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 (eg &dyn MyTrait
), and pass a reference to a MyStruct
object to it, what happens is the following:
MyStruct
implementations of f
and g
.&dyn MyTrait
reference, hence the reference will be twice its usual size; sometimes &dyn
references are called "fat references" for this reason.f
and g
will then result in indirect function calls using the pointers stored in the vtable.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:
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) { ... }
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.
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.
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.
Apart from that, all things being equal, my advice is to use monomorphism if you can, and dynamic dispatch if you have to. Personally, I've noticed that in much of my code, I only end up calling the function once or twice. If you're only calling it once, you're getting monomorphism for free, since the binary code will only be generated once. On the other hand, using a dyn
reference might result in simpler code — your mileage may vary.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.