如何在枚举中匹配自我?

The*_*ler 13 enums pattern-matching rust

我有一个枚举:

enum Expr {
    Lit(u32),
    Var(Id),
    Ass(Id, u32),
    Add(u32, u32),
    Sub(u32, u32),
    Mul(u32, u32),
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试实现一个方法:

impl Expr {
    fn eval(&self, env: &mut Env) -> Result<u32, String> {
        use Expr::*;

        match *self {
            Lit(l) => Ok(l),
            Var(id) => env.lookup(&id).ok_or_else(|| format!("undefined var {:?}", id)),
            Ass(id, v) => {
                env.assign(id, v);
                Ok(v)
            }
            Add(f, s) => Ok(f + s),
            Sub(f, s) => Ok(f - s),
            Mul(f, s) => Ok(f * s),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但我收到以下错误:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:25:15
   |
25 |         match *self {
   |               ^^^^^ cannot move out of borrowed content
26 |             Lit(l) => Ok(l),
27 |             Var(id) => env.lookup(&id).ok_or_else(|| format!("undefined var {:?}", id)),
   |                 -- hint: to prevent move, use `ref id` or `ref mut id`
28 |             Ass(id, v) => {
   |                 -- ...and here (use `ref id` or `ref mut id`)
Run Code Online (Sandbox Code Playgroud)

没有明星,我也会收到一个错误:

error[E0308]: mismatched types
  --> src/main.rs:25:17
   |
25 |                 Lit(l) => Ok(l),
   |                 ^^^^^^ expected &Expr, found enum `Expr`
   |
   = note: expected type `&Expr`
   = note:    found type `Expr`
Run Code Online (Sandbox Code Playgroud)

我想我理解了第一个错误:我试图做的不仅仅是我允许使用(不可变)借用self,但我不确定第二个错误.我不知道如何正确地做到这一点.

ant*_*oyo 14

对于第一个问题,您需要使用ref@Adrian所说的关键字:

impl Expr {
    fn eval(&self, env: &mut Env) -> Result<u32, String> {
        use Expr::*;

        match *self {
            Lit(l) => Ok(l),
            Var(ref id) => env.lookup(id).ok_or_else(|| format!("undefined var {:?}", id)),
            Ass(ref id, v) => {
                env.assign(id.clone(), v);
                Ok(v)
            }
            Add(f, s) => Ok(f + s),
            Sub(f, s) => Ok(f - s),
            Mul(f, s) => Ok(f * s),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用ref可防止模式匹配获得所有权id.至于你提到,你是不是允许采取的价值id出来的Expr,因为你只能有一个不变的参考.v,f并且s没有这个问题,因为它们是u32实现的Copy.它们不是取出价值,而是复制,留下原件.

我不知道什么EnvId类型的,或者的定义lookupassign,因此,或许一些clone()调用是没有必要的.

对于你的第二个问题,这是因为self是类型&Expr,所以你需要包含&在模式中:

impl Expr {
    fn eval(&self, env: &mut Env) -> Result<u32, String> {
        use Expr::*;

        match self {
            &Lit(l) => Ok(l),
            &Var(ref id) => env.lookup(id).ok_or_else(|| format!("undefined var {:?}", id)),
            &Ass(ref id, v) => {
                env.assign(id.clone(), v);
                Ok(v)
            }
            &Add(f, s) => Ok(f + s),
            &Sub(f, s) => Ok(f - s),
            &Mul(f, s) => Ok(f * s),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

两种形式的匹配都是等价的,但*self更惯用,需要更少的输入:)

  • Rust 语言似乎发生了一些变化,因为从 2021 年开始,星号不再是必需的,第一个带有“match self”的变体可以工作。 (3认同)