1 个不稳定版本
0.0.0 | 2018年12月18日 |
---|
#26 在 #shadow
在 traitor 中使用
10KB
200 行
Traitor:对象特质vtable生成
Traitor是一个库,允许通过将任何预存在的数据类型绑定到任何类型的元信息来生成特质对象。
它使用了一些关于实现细节的假设,因此不是100%无懈可击的。
假设我们想要通过给一个实例附加一些“颜色”信息来生成Colored
特质对象。
#[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());
}
许可证
以下任一许可证下使用
- Apache License,版本2.0,(LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非你明确声明,否则根据Apache-2.0许可证定义,任何有意提交以包含在你作品中的贡献,将根据上述方式双重许可,没有额外的条款或条件。
依赖
~2MB
~46K SLoC