3个不稳定版本
0.2.0 | 2023年12月5日 |
---|---|
0.1.1 | 2023年12月3日 |
0.1.0 | 2023年12月3日 |
#636 in 过程宏
13KB
184 行
enumify
一个基于类型集合声明枚举(及一系列实现From trait)的Rust宏。
示例
有两个典型的例子,其中enumify的代码生成可以大大减少样板代码:**在枚举上进行分发**和**抽象语法树**。
在枚举上进行分发
当与线程一起工作时,一个非常常见的方法是将它们组织成**代理**:线程通过异步通道(如crossbeam::channel
或tokio::sync::mpsc
)进行**消息传递**,并且线程有一个预先确定的可以处理的消息集。这种方法使用一个类似于以下代码片段的“主循环”
while let Ok(message) = receiver.recv() {
match message {
Message::Foo(foo) => self.handle_foo(foo),
Message::Bar(bar) => self.handle_bar(bar),
Message::Oof(oof) => self.handle_oof(oof),
}
}
虽然从表面上看这看起来无害且相当令人愉快,但一个漫不经心的观察者却不知道为了声明每个变体的struct
和包含每个struct
变体的enum Message
,手动编写了多少样板代码。这已经足够有说服力了,但通常还有一大堆等待实现的impl From
。
enumify
通过自动生成重复部分来解决此问题。例如,声明 Message
和相关的转换就像声明每个变体对应的 struct
一样简单
enumify::enumify! {
#[derive(Debug)]
pub enum Message;
#[derive(Debug)]
pub struct Foo {
here: usize,
there: usize,
}
#[derive(Debug, Deserialize)]
pub struct Bar {
one: usize,
#[serde(rename = "another")]
other: usize,
}
#[derive(Debug)]
pub struct Oof(String);
}
您可以声明针对 enum
和 struct
的 impl
,就像您自己声明它们一样
impl Foo {
pub fn sum(&self) -> usize {
self.here + self.there
}
}
自动推导的转换使得构建新的消息变得同样简单
pub fn send(sender: Sender<Message>, message: impl Into<Message>) {
sender.send(message.into()).expect("looks good");
}
impl Foo {
pub fn do_the_thing(&self, sender: Sender<Message>) {
let message = Message::from(Bar {
one: 1 + self.here,
other: 11 + self.there,
});
sender.send(message).expect("looks good");
}
pub fn do_another_thing(&self, sender: Sender<Message>) {
send(sender, Bar {
one: 1 + self.here,
other: 11 + self.there,
});
}
}
抽象语法树
在 Rust 中处理递归类型总有一点令人烦恼。这里有一个 Box
,那里有一个 Rc
,通常没问题。问题是这种烦恼随着需要包装的数量线性增长。这种烦恼达到临界点的案例之一就是抽象语法树。幸运的是,enumify
也可以在这里帮助我们:在某个 struct
顶部添加 #[enumify(Wrapper)]
属性,将对应于 enum
的变体包装在具有在属性中指定的特定类型的 struct
中。例如,声明无类型 lambda 演算的类型看起来如下所示
enumify::enumify! {
#[derive(Debug)]
pub enum Term;
#[derive(Debug)]
pub enum Var {
Free(String),
Bound(usize),
}
#[enumify(Box)]
#[derive(Debug)]
pub struct App {
function: Term,
argument: Term,
}
#[enumify(Box)]
#[derive(Debug)]
pub struct Abs {
variable: String,
body: Term,
}
}
impl From<String> for Term {
fn from(value: String) -> Self {
Self::from(Var::Free(value))
}
}
impl From<&str> for Term {
fn from(value: &str) -> Self {
Self::from(Var::Free(value.to_owned()))
}
}
#[test]
fn it_works() {
let _ = Term::from(App {
function: Term::from(Abs {
variable: String::from("x"),
body: Term::from("x"),
}),
argument: Term::from("y"),
});
}
依赖关系
~315–770KB
~18K SLoC