1 unstable release

0.0.0 Dec 18, 2018

#31 in #shadow


Used in traitor

MIT/Apache

10KB
200 lines

Traitor: object trait vtable generation

Traitor is a library that allows to generate trait objects by binding any pre-existing data type to any kind of meta-information.

It uses some assumptions about implementation details, so is not 100% bulletproof.

Let's say we want to generate Colored trait object for an instance of String by attaching some "color" information to it.

#[derive(Clone, Debug, PartialEq)]
enum Color {
    Red,
    Green,
    Blue,
    /// It's possible to attach any kind of data!
    Other(String),
}

/// Marking with a `traitor::shadow` generates necessary "glue" code.
#[traitor::shadow]
trait Colored {
    /// Any object-safe function is okay. However, currently it is required that `&self` is
    /// marked with an explicit lifetime.
    fn color<'data>(&'data self) -> Color;
}

/// `ColoredShadow` is the shadow trait generated by proc macro. It's marked by `unsafe` because
/// the whole mechanism is sketchy and only works in limited cases.
///
/// The main idea is that it mirrors the functions of the original trait, but every function in
/// addition receives a reference to the "metadata", where "metadata" is an arbitrary user
/// defined data. This data moves into the library internal data structures via "declare"
/// operation.
unsafe impl ColoredShadow for Color {
    /// Associated type indicates which data type this "shadow" can attach to.
    type Data = String;

    /// In the shadow trait, each function is the same as in the original trait with the two
    /// differences:
    /// 1. First argument becomes reference to the data instead of `&self`. The value passed
    /// here is `&self` reference on which `Colored::color` trait object function is invoked.
    /// 2. An additional argument is added at the end. This argument is the reference to the
    /// metadata we pre-allocated. It's of the type `Self`, therefore this trait is supposed to
    /// be implemented on the metadata type (`Color` in our case).
    fn color<'data>(_data: &'data String, meta: &'data Self) -> Color {
        // Juts return a clone of ourselves!
        meta.clone()
    }
}

fn main() {
    let traitor = traitor::Traitor::new();

    // `declare` function takes metadata wrapped into the `[*]ShadowInfo` struct (which is
    // another item generated by `shadow` proc macro) and returns a "binder". Each metadata is
    // moved to an internal arena managed by the `Traitor` instance. "binder" provides a
    // `bind` function which takes a reference to the data and returns a trait object.
    // "Declaring" a binding allocates memory for internal data structures and for the
    // attached "metadata".
    let binder = traitor.declare(ColoredShadowInfo::new(Color::Other("turquoise".into())));
    let data = "hello".to_string();

    // "Binding" does not allocate anything, but simply transforms reference to a fat trait
    // object reference.
    let bound: &Colored = binder.bind(&data);
    assert_eq!(Color::Other("turquoise".into()), bound.color());
}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~2MB
~48K SLoC