rustasync-awaittraitsdynamic-dispatch

Why is rust async forcing the implementors to use dynamic dispatch for `Waker`


I was reading about how async/await was implemented in rust and I noticed that the RawWaker is essentially a fat pointer. I don't quite understand why the Waker is implemented essentially like struct Waker{data: *const (), vtable: *const WakerVTable} rather than Waker<T: Wakes>{raw_walker: &mut T} which would allow the compiler to know the implementations of wake(), wake_ref(), drop() and clone() at compile time.


Solution

  • Wakers need to be incredibly versatile. This blog explains a lot about the design choices, and it's worth a read. The relevant part says this:

    The only requirement that the poll phase introduces is dynamism: because it needs to be passed through arbitrary futures, the waker type cannot be generic. This means that every requirement introduced by the other two phases needs to be dynamically dispatched.

    Now the part that the author wasn't clear on here is that "arbitrary futures" includes boxed futures (and futures which can be polled using dynamic dispatch in general). If the definition of the future trait were the following:

    pub trait Future {
        type Output;
    
        fn poll<W: Wake>(
            self: Pin<&mut Self>,
            cx: &mut Context<'_, W>,
        ) -> Poll<Self::Output>;
    }
    

    then the trait Future would not be object safe, eliminating the possibility of dynamic dispatch, which was a deal-breaker.