与 Box 层的模式匹配

rwa*_*ace 5 pattern-matching rust sum-type

我正在尝试解决 Rust 中的表达问题。我定义了一个总和类型的术语:

#[derive(Clone, Debug, PartialEq)]
pub enum Term {
    True,
    False,
    Not(Box<Term>),
    ...
}
Run Code Online (Sandbox Code Playgroud)

编译器和文档说Box递归术语是必需的,因为结构不能包含自身(无限回归),并且仅仅简单地&Term不足以确定术语拥有其子术语。好的,到目前为止一切顺利。

现在我正在尝试编写一个函数,根据运算符的定义简化术语,例如 not true = false:

impl Term {
    pub fn simplify(self) -> Term {
        let a = self.map(Term::simplify);
        match a {
            Term::Not(Box(Term::True)) => Term::False,
            _ => a,
        }
    }

    pub fn map(self, f: fn(Term) -> Term) -> Term {
        match self {
            Term::True
            | Term::False => self,
            Term::Not(a) => Term::Not(Box::new(a.map(f))),
            _ => panic!(),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但编译器不喜欢我迄今为止尝试过的任何版本。

Term::Not(Term::True)无效,因为 aBox需要介于两者之间。

Term::Not(Box::new(Term::True))在创建术语时有效,但作为模式匹配表达式(不能包含函数调用)时无效。

Term::Not(Box(Term::True))也无效。

在 Rust 中执行此操作的正确方法是什么?

小智 5

编译器给出以下错误: cannot match against a tuple struct which contains private fields。好的,让我们看看Box的定义(为了简单起见,我删除了特征边界):

pub struct Box<_>(Unique<T>, A);
Run Code Online (Sandbox Code Playgroud)

这看起来像错误消息中的元组。但它看起来内部值也不是公开的(这就是错误),所以你不能像这样构造盒子(Box(Term::True))。

我们可以做什么?您可以使用夜间功能box_patterns来创建盒子:

match a {
    Term::Not(box Term::True) => Term::False,
    _ => a,
}
Run Code Online (Sandbox Code Playgroud)

操场

或者,我们尝试通过取消引用将值从盒子中提取出来(此处为boxed_value),然后检查内部值:

*boxed_value == Term::True
Run Code Online (Sandbox Code Playgroud)

您可以将其与以下内容结合if guards使用match

match a {
    Term::Not(content) if *content == Term::True => Term::False,
    _ => a,
}
Run Code Online (Sandbox Code Playgroud)

我认为这种变体更好,特别是如果您还想从 映射Term::FalseTerm::True

操场