Welcome
The dyner
crate is an experimental crate that is exploring the "outer reaches" of how we can improve trait objects (dyn
types) in Rust.
dyner
makes it possible to use dynamic dispatch on traits that...
- ...have async functions.
- ...use
impl Trait
in argument or return position, so long asTrait
is also processed withdyner
. - ...have by-value
self
methods.
More dyner features:
- No need to worry about
?Sized
! 🧑🍳 😘
Quick start
To give you an idea for how dyner works, consider this pair of traits:
#![allow(unused)] fn main() { #[dyner] trait Screen { fn put(&mut self, ch: char, x: u32, y: u32); } #[dyner] trait Draw { fn draw(&self, screen: &mut impl Screen); } }
You can't use ordinary dyn
types with these traits. But because we added the #[dyner]
annotation, we have access to two types, DynScreen
and DynDraw
, that we can use to get dynamic dispatch. For example, we might collect a vector of "drawable things":
#![allow(unused)] fn main() { fn make_drawables() -> Vec<DynDraw<'static>> { let r = Rectangle::new(0, 0, 10, 10); let c = Circle::new(22, 44, 66); vec![DynDraw::new(r), DynDraw::new(c)] } }
The DynDraw<'static>
type here means "some Draw
object that has no references". You could also write a routine that draws these things onto a screen:
#![allow(unused)] fn main() { fn draw_all(draws: &[DynDraw<'_>], screen: &mut impl Screen) { for draw in draws { draws.draw(screen); } } }
Note that this version of draw_all
used an impl Screen
, and hence we would create a distinct version of this method for every kind of screen. To avoid that, maybe we want to use dynamic dispatch there too. No problem, just change impl Screen
to DynScreen<'_>
, and everything still works:
#![allow(unused)] fn main() { fn draw_all(draws: &[DynDraw<'_>], screen: &mut DynScreen<'_>) { for draw in draws { draws.draw(screen); } } }
Objects from references
In the previous examples, we used DynDraw::new
to construct an object; the new
method takes ownership of the data in the object. But sometimes we just have an &impl Trait
and we'd like to get dynamic dispatch from that. You can use the from_ref
method to do that. In this code, the draw_four
method is implemented with impl Trait
, but it calls into the draw_two
method, which is implemented with dynamic dispatch:
#![allow(unused)] fn main() { fn draw_four(draw: &impl Draw, screen: &mut impl Screen) { let draw_ref = DynDraw::from_ref(draw); let screen_mut = DynScreen:from_mut(screen); draw_two(&draw_ref, screen); draw_two(&draw_ref, screen); } fn draw_two(draw: &DynDraw<'_>, screen: &mut DynScreen<'_>) { draw.draw(screen); draw.draw(screen); } }
No std? No problem.
By default, DynDraw
depends on std, since DynDraw::new
needs to allocate a Box
behind the scenes. If you opt out from the default features, you will lose access to DynDraw::new
, but you can still use DynDraw::from_ref
and DynDraw::from_mut
.
User's guide
Plumbing
FAQ
Why is it called dyner?
Honestly? dyno was taken. Best name ever! Think of the logo! It practically draws itself!
But dyner is cool too: it's more dyn than dyn!
How do I pronounce dyner, dude?
Pronounce it like "diner".
Or maybe "dinner".
Whatever floats your boat!
Does dyner have a theme song?
Why, it does! So glad you asked! It goes to the tune of, "I've been working on the railroad".
I've been working on my rust code
All the live long day
Got a problem in my Rust code
Static dispatch is in the way
Too many copies of my function
One for every type I use
Mono-, monomorphization
It's... so easy to abuse
Dyner up my code
Dyner up my code
Dyner up the traits in my coh-oh-ode
Dyner up my code
Dyner up my code
Dyner up the traits in my code
Dynamic dispatch with dyner
It's pretty easy to do-ooh-ooh
Dynamic dispatch with dyner
It's fun for me and you
D, Y, N E R
D, Y, N E R-R-R-R
D, Y, N E-eeee Rrrrr
Dynamic dispatch in my code
Does dyner
work for no-std crates?
Yes! But without access to the Box
type, you can only pass trait objects that are stored in references.
Wouldn't it be nicer if you built this stuff into the language?
Maybe! But first we have to decide what we want to build. There are also some aspects of trait objects that are necessarily quite "custom", so it may be that there would still be a role for a crate like dyner
even if we were to change how dyn
works in Rust.
Is there more you would like to do with dyner?
Yes! Here are some things we would like to support but haven't gotten around to yet:
- Supports for arbitrary smart pointers. In particular, it'd be great if you could use
from_ref
with anythingimpl Deref<Target: Trait>
, or at least anyimpl Deref
that is pointer sized. Not clear how to best express or manage invoking the destructor. - No-std compatibility! That... could be hard. It's not clear what's the best way to return a future if boxing is not available.