【问题标题】:How to match over self in an enum?如何在枚举中匹配自身?
【发布时间】:2016-02-28 18:11:31
【问题描述】:

我有一个枚举:

enum Expr {
    Lit(u32),
    Var(Id),
    Ass(Id, u32),
    Add(u32, u32),
    Sub(u32, u32),
    Mul(u32, u32),
}

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

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),
        }
    }
}

但我收到以下错误:

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`)

没有星号,我也会收到错误消息:

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`

我想我理解第一个错误:我试图做的比我所允许的更多(不可变)借用 self,但我不太确定第二个错误。我不知道如何正确地做到这一点。

【问题讨论】:

  • 您也可以使Expr 可复制。或者,如果您在评估后不再需要它,则在评估期间使用它。

标签: enums pattern-matching rust


【解决方案1】:

对于第一个问题,你需要使用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),
        }
    }
}

使用ref 可以防止模式匹配获得id 的所有权。正如您所提到的,您不能将 id 的值从 Expr 中取出,因为您只有一个不可变的引用。 vfs没有这个问题,因为它们是u32,它实现了Copy。不是取出值,而是复制它们,保留原件。

我不知道EnvId 类型是什么,或者lookupassign 的定义,所以可能不需要一些clone() 调用。

对于第二个问题,这是因为self 的类型为&amp;Expr,因此您需要在模式中包含&amp;

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),
        }
    }
}

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

【讨论】:

  • 看起来 Rust 语言发生了一些变化,因为从 2021 年起不需要星号,第一个带有“匹配自我”的变体有效。
  • 是的,这称为匹配人体工程学,但我不建议使用它,因为它会提供令人困惑的错误消息。
猜你喜欢
  • 2011-12-21
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-17
相关资源
最近更新 更多