Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
352 views
in Technique[技术] by (71.8m points)

pattern matching - How to match over self in an enum?

I have an enum:

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

I'm trying to implement a method:

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

but I'm getting the following error:

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

Without the star, I'm also getting an error:

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`

I think I understand the first error: I'm trying to do more than I'm allowed with the (immutable) borrowed self, but I'm not really sure about the second error. I have no idea how to do this properly.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

For the first question, you need to use the ref keyword, as said by @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),
        }
    }
}

Using ref prevents the pattern matching from taking ownership of id. As you mention, you are not allowed to take the value of id out of the Expr because you only have an immutable reference. v, f, and s don't have this problem because they are u32, which implements Copy. Instead of taking the value out, they are copied, leaving the original in place.

I don't know what the Env or Id type are, or the definitions of lookup and assign, so perhaps some clone() calls are not necessary.

For your second question, this is because self is of type &Expr, so you need to include & in the patterns:

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

Both forms of matching are equivalent, but *self is more idiomatic and requires less typing :)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...