1 个不稳定版本
0.1.0 | 2021年11月13日 |
---|
#9 在 #duck
在 constduck 中使用
6KB
111 行
constduck
:编译时鸭子类型和反射
constduck
提供了一个过程宏,可以启用对任意结构体类型的编译时鸭子类型和反射。它支持三个主要功能
- 使用字段名访问任何结构体的字段
- 从字段到值的映射中构造实例
- 在编译时反映字段类型
使用方法
在结构体上派生 ConstDuck
#![feature(adt_const_params)]
use constduck::*;
#[derive(ConstDuck)]
struct Donald {
money: i64,
}
访问字段
当派生 ConstDuck
时,为结构体的所有字段实现了 Field<"fieldname">
特性。您可以使用该特性编写泛型实现。例如
fn deduct_money<N, T: Field<"money", Ty = N>>(t: &mut T)
where N: Clone,
N: Sub<N, Output = N>,
N: From<i8> {
t.set(t.get().clone() - N::from(5i8));
}
deduct_money
将适用于任何具有字段 money
并派生 ConstDuck
的结构体。
您只需为上面的示例编写一个自定义特性。并非总是可以编写自定义特性。例如,考虑以下宏
macro_rules! make_getter {
($struct:ident.$field:ident) => {
impl $struct {
pub fn $field(&self) -> &/* What to write here? */ {
&self.$field
}
}
}
}
struct Foo {
bar: String,
baz: u32,
}
make_getter!(Foo.bar);
在这种情况下,函数定义需要一个返回类型,但您没有足够的信息来指定类型。使用 constduck
,您可以像这样编写此宏
macro_rules! make_getter {
($struct:ident.$field:ident) => {
impl<T> $struct
where Self: Field<{ stringify!($field) }, Ty = T> {
pub fn $field(&self) -> &T {
<Self as Field<{ stringify!($field) }>>::get(self)
}
}
}
}
#[derive(ConstDuck)]
struct Foo {
bar: String,
baz: u32,
}
make_getter!(Foo.bar);
构造实例
如果您在宏中存储新结构体的任意表达式,则不能使用表达式的类型作为字段的类型。例如
macro_rules! create_struct {
($struct:ident; $($name:ident: $value:expr),*) => {{
// For some good reason we want to generate a separate wrapper struct first and return that instead of collecting directly into `$struct`.
struct WrapperStruct<$($name,)*> {
$($name: $name,)*
}
impl<$($name,)*> From<WrapperStruct<$($name,)*>> for $struct {
fn from(wrapper: WrapperStruct<$($name,)*>) -> Self {
// wrapper.counter is not guaranteed to be a u32
// So we cannot do this:
$struct {
counter: wrapper.counter,
text: wrapper.text,
}
}
}
WrapperStruct {
$($name: $value,)*
}
}}
}
我们无法在这里编写 From
的正确实现,因为通常无法将“类型必须有两个字段 counter: u32
和 text: String
” 表达为一个约束。
通过为包装结构体实现 WithField
,我们可以在宏中像这样编写实现
impl<$($name,)*> From<WrapperStruct<$($name,)*>> for $struct
where $struct: ConstructFrom<WrapperStruct<$($name,)*>> {
fn from(wrapper: WrapperStruct<$($name,)*>) -> Self {
$struct::construct(wrapper)
}
}
请参考完整的示例,见 constduck/examples/construct-instances.rs
。
在编译时反映字段类型
使用 ConstDuck::Reflect
,您可以为任何类型(如 #[derive(..)]
))实现特质,无需使用procmacro。请参考 constduck/examples/debug-print.rs
中的示例。
(不)稳定性
此项目需要Rust夜间版本,并使用不完整的 adt_const_params
功能。您可能会遇到ICEs。当前API在添加对元组和枚举的支持时可能会破坏。
许可证
constduck
采用Mozilla公共许可证2.0(MPL2.0)。请参阅LICENSE
文件。
依赖关系
~1.5MB
~35K SLoC