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
.