When both options are possible, should I prefer to use dyn
or impl
? I know that dyn
has a (small, and possibly optimized out) runtime cost while impl
has no runtime cost, but can generate code bloat and may slow the compilation. What should be the rule of thumb?
When choosing the return value of a function, I almost always go for impl Trait
as the return type when it is possible.
And for input parameters, especially for references?
For input parameters I prefer generics over dynamic dispatch.
So impl in all cases. This make sense.
I prefer to write it using generics instead of impl trait, but that is just a style difference.
Except for the syntax itself, fn <T: SomeTrait>(t: &mut T)
and fn(t: &mut impl SomeTrait)
are exactly the same, aren't they?
There is one difference, actually: You can only use turbofish syntax with the former. That is not why I prefer it though — I think &mut impl Trait
is misleading and looks too much like &mut dyn Trait
.
If input by reference then use &[mut] (impl ?Sized + Trait)
(or <T : ?Sized + Trait> ... &[mut] T
if turbofish matters). This way you support both the ergonomics and runtime performance of static dispatch, while keeping the possibility for downstream users to monomorphise to T = dyn Trait
exclusively when compile time and code bloat are an issue.
-
Given that closures are unnameable (except for
fn
pointers), including them for turbofish is actually counter productive, so in that case I personally favor theimpl
syntax; -
Depending on the trait and its usage, I may directly use
dyn
in some cases, such as when dealing withDisplay
orWrite
(&'_ dyn Display
and&'_ mut dyn Write
); -
In return position, like @alice said, one ought to favor
impl Trait
(orSelf::AssocType
withAssocType : Trait
when implementing a trait's function / method), sinceBox<dyn Trait>
involves an allocation (except when the type being boxed is zero-sized), and that people wanting aBox<dyn Trait + '_>
can simply box the return type themselves. -
When the trait is not object-safe, only generics can be used (although the main reason for a trait not to be object safe is for it to be using geenric parameters in its methods to begin with).
-
In practice the runtime performance of virtual dispatch is very often negligible, so resorting to
& [mut ] dyn Trait
by default for input parameters is a strategy some developers advise.-
For instance, many
async
/Future
-heavy libraries and applications get away with using#[async_trait]
, despite its leading to moreBox
ing and virtual dispatch (all theasync fn
methods in a trait get transformed to:) -> Box<dyn Future...> { Box::new(async move { ... }) }
-
Thanks a lot for the details.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.