Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 880
The Oddler Avatar asked Nov 25 '15 19:11

The Oddler


1 Answers

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

like image 181
antoyo Avatar answered Nov 11 '22 04:11

antoyo