1 unstable release
0.0.0 | Dec 18, 2018 |
---|
#31 in #shadow
Used in traitor
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
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
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