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 as Trait is also processed with dyner.
  • ...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 anything impl Deref<Target: Trait>, or at least any impl 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.