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?
Generics have to prove they are bioequivalent to the brand version. Bioequivalence means the generic works the same way and provides the same benefits. It’s the FDA’s job to monitor drug safety.
Original drug, as the name suggests, is a new drug that has never been created before. It has a patent period of 20 years and hence is also called a patent drug. However, when the patent expires, other drug manufacturers can start produce this new drug, and the drug they produced is called generic drug.
A Non-generic collection is a specialized class for data storage and retrieval that provides support for stacks, queues, lists and hashtables. The key difference between Generic and Non-generic Collection in C# is that a Generic Collection is strongly typed while a Non-Generic Collection is not strongly typed. 1. Overview and Key Difference 2.
The Generic Dictionary in C# is a collection of keys and values. When there is a statement as follows, the object dictionary1 can store int type keys and string type values. A Generic SortedList collection stores key and value pairs in ascending order of key by default.
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 (e.g. &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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With