/// Here is some metadata we want to attach to String instances! #[derive(Clone, Copy, Debug, PartialEq)] pub enum Color { Red, Green, Blue, } /// This is the trait object we want to attach to string. /// Attribute is where magic happens -- it tells library to generate "shadow" trait and some /// boilerplate to allow for binding to the trait at runtime. #[traitor::shadow] trait Colored { /// We want our data item to tell which color is it! /// Note that an explicit lifetime annotation is required for `&self`, so our proc macro /// can use it for the "meta" reference, too. 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. unsafe impl ColoredShadow for Color { type Data = String; /// In the shadow trait, each function is the same as in the original trait with 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 copy of ourselves! *meta } } /// Another item generated is the `[*]ShadowInfo` struct, (`ColoredShadowInfo`). This struct /// contains some details needed by the library. #[test] fn test() { let traitor = traitor::Traitor::new(); // `declare` function takes metadata wrapped into the `[*]ShadowInfo` struct and returns a // "binder". Each metadata is allocated in an internal arena in the library. Each "binder" // provides two functions `bind` and `bind_mut` which take reference to the data and return // trait object back. let red_binder = traitor.declare(ColoredShadowInfo::new(Color::Red)); let green_binder = traitor.declare(ColoredShadowInfo::new(Color::Green)); let blue_binder = traitor.declare(ColoredShadowInfo::new(Color::Blue)); // Cannot do that! All our bindings cannot outlive `Traitor` itself because it stores our // metadata in an internal arena and manages some internal structures to support these bindings. //std::mem::forget(traitor); let data = "hello".to_string(); let red_hello: &Colored = red_binder.bind(&data); let green_hello: &Colored = green_binder.bind(&data); let blue_hello: &Colored = blue_binder.bind(&data); // Behavior is different for each trait object even though data type (and even the reference // itself) are the same! assert_eq!(Color::Red, red_hello.color()); assert_eq!(Color::Green, green_hello.color()); assert_eq!(Color::Blue, blue_hello.color()); // Data pointers are all the same! let ptr = &data as *const String as *const (); assert_eq!(ptr, extract_data_ptr(red_hello)); assert_eq!(ptr, extract_data_ptr(green_hello)); assert_eq!(ptr, extract_data_ptr(blue_hello)); } fn extract_data_ptr(colored: &Colored) -> *const () { // Take the first part of the fat trait object pointer, it's the pointer to the data // (assumes current implementation detals!) unsafe { *(&colored as *const &Colored as *const *const ()) } }