Crate tinydyn

source ·
Expand description

Lightweight dynamic dispatch, intended for embedded use.

Ref<dyn Trait> and RefMut<dyn Trait> wrap a pointer and metadata necessary to call trait methods, and Deref into a tinydyn trait object that implements the Trait.

Traits must currently opt-in by annotating with tinydyn. This defines an alternate, lighter weight vtable, and if the trait has one method, eliminates it entirely by putting the function pointer inline. This does not affect normal behavior of the trait, and can still be made into a dyn Trait. This, however, would be wasteful.

Example

use tinydyn::{tinydyn, Ref, RefMut};

#[tinydyn]
trait Spam {
    fn ham(&mut self) -> i32;
    fn eggs(&self) -> i32 { 10 }
}

impl Spam for i32 {
    fn ham(&self) -> i32 {
        *self += 2;
        *self - 1
    }
}

let mut x = 15;

// Like upcasting to `&dyn Foo`, but with a lighter weight vtable.
let mut mutable: RefMut<dyn Foo> = RefMut::new(&mut x);
assert_eq!(mutable.ham(), 16);
assert_eq!(mutable.eggs(), 10);

// mutable.into() would instead consume and have the same lifetime as `mutable`
let shared: Ref<dyn Foo> = mutable.as_ref();
assert_eq!(shared.eggs(), 10);
// Cannot call `shared.ham()` as it's a shared ref and can't call `&mut self` methods.

assert_eq!(x, 17);

Planned features

⚠️ This library is not yet tested enough to be production ready ⚠️

  • &self and &mut self methods
  • + Send and + Sync trait objects
  • lifetime where bounds on methods
  • lifetime generics on methods
  • implementing on foreign traits/custom vtables
  • implementations for common core/std traits (never core::fmt::{Debug, Display} as they use &dyn)
  • generics on the trait
  • associated types
  • supertraits
    • upcasting Ref<dyn Subtrait> to Ref<dyn Supertrait>
  • Pin<&mut self> and similar non-reference object-safe receivers
  • where bounds on the trait
  • where Self: Sized methods (and appropriate exclusion from the vtable)
    • non-lifetime generics on methods
    • non-lifetime where bounds on methods
    • An attribute to manually exclude a method from a vtable, necessary for bounds including subtraits or aliases of Sized
  • An tinydyn(inline_vtable[ = "all"]) attribute to force inlining of the vtable into the wide pointer. This would require the metadata type to always be carried in the trait.
  • Put Ref vtables inline even if RefMut won’t. Ex: 1 &self and 1 &mut self method.
  • UI tests to ensure proper rejection and error message quality

Implementing on foreign traits

Implementing on foreign traits is not yet supported, and is an ergonomic and safety challenge to get right, especially with regards to default methods. As a workaround, you can create a local helper trait that contains the desired methods and blanket implements for all T: TargetTrait. This functionality might be added by tinydyn in the future, or a better solution like defining custom local vtables for foreign traits. Traits with supertraits that wish to use this version of tinydyn have a similar workaround.

Design

See the README for how this library is designed and works.

Structs

  • A shared reference to a tinydyn trait object.
  • A mutable reference to a tinydyn trait object.

Traits

  • Builds the tinydyn trait metadata for a given type.
  • A trait object that works with tinydyn, including any extra bounds.
  • Types that could be cast to the given Trait trait object.
  • A trait object supported by tinydyn.

Attribute Macros

  • Marks a local trait as tinydyn-aware, letting it be used inside of Ref and RefMut.