Skip to content

loichyan/dynify

Repository files navigation

🦕 dynify

crates.io docs.rs msrv build status codecov

Add dyn compatible variant to your async trait with dynify!

✨ Overview

dynify implements partial features of the experimental in-place initialization proposal in stable Rust, along with a set of safe APIs for creating in-place constructors to initialize trait objects. Here’s a quick example of how to use dynify:

use dynify::{from_fn, Dynify, Fn};
use std::future::Future;
use std::mem::MaybeUninit;

// `AsyncRead` is dyn incompatible :(
trait AsyncRead {
    async fn read_to_string(&mut self) -> String;
}

// With dynify, we can create a dyn compatible variant for `AsyncRead` in a few lines :)
trait DynAsyncRead {
    fn read_to_string(&mut self) -> Fn!(&mut Self => dyn '_ + Future<Output = String>);
}
impl<T: AsyncRead> DynAsyncRead for T {
    fn read_to_string(&mut self) -> Fn!(&mut Self => dyn '_ + Future<Output = String>) {
        from_fn!(T::read_to_string, self)
    }
}

// Now we can use dynamic dispatched `AsyncRead`!
async fn dynamic_dispatch(reader: &mut dyn DynAsyncRead) {
    let mut stack = [MaybeUninit::<u8>::uninit(); 16];
    let mut heap = Vec::<MaybeUninit<u8>>::new();
    // Initialize trait objects on the stack if not too large, otherwise on the heap.
    let fut = reader.read_to_string().init2(&mut stack, &mut heap);
    let content = fut.await;
    // ...
}

For a more detailed explanation, check out the API documentation.

🔍 Comparisons with other similar projects

vs pin-init

pin-init has been around for a while and provides safe methods for creating in-place constructors for structs. It also has an experimental branch that enables the generation of dyn compatible variants for async fns. The key difference is that pin-init relies on some nightly features, while dynify is built with stable Rust. Moreover, as their names suggest, pin-init is focused on the pinned initialization of structures, whereas dynify targets dyn compatibility for functions. With its ongoing #[dyn_init] feature, pin-init can be considered as a superset of dynify.

vs async-trait

async-trait is another widely used crate for dynamic dispatch on AFIT (Async Fn In Trait). The main advantage of dynify is its ability to allocate trait objects on the stack, making it more suitable for limited environments. In contrast, async-trait requires heap allocation to store trait objects, as it essentially transforms async fn into Box<dyn Future>.

♥️ Special thanks

  • Rust-for-Linux/pin-init for its brilliant design on creating constructors for async fns, which serves as the foundation of dynify.
  • In-place initialization proposal for its excellent design on initializer traits, which is incorporated into several trait designs of dynify.
  • zjp-CN/dyn-afit for the comprehensive comparisons of community solutions for dynamic dispatch on AFIT, which greatly inspired dynify.

⚖️ License

Licensed under either of

at your option.

About

🦕 Add dyn compatible variant to your async trait!

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published