#枚举 #语法树 #AST # #实现 #样板代码 #集合

enumify

一个基于类型集合声明枚举(及一系列实现From trait)的Rust宏。

3个不稳定版本

0.2.0 2023年12月5日
0.1.1 2023年12月3日
0.1.0 2023年12月3日

#636 in 过程宏

MIT 许可证

13KB
184

enumify

一个基于类型集合声明枚举(及一系列实现From trait)的Rust宏。

示例

有两个典型的例子,其中enumify的代码生成可以大大减少样板代码:**在枚举上进行分发**和**抽象语法树**。

在枚举上进行分发

当与线程一起工作时,一个非常常见的方法是将它们组织成**代理**:线程通过异步通道(如crossbeam::channeltokio::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);
}

您可以声明针对 enumstructimpl,就像您自己声明它们一样

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