Help with move and enum destructuring


#1

Help me understand move semantics when destructuring enums.
I have following types:

pub enum Expr {
    Number(i32),
    Op(Box<Expr>, Opcode, Box<Expr>),
}

pub enum Opcode {
    Mul,
    Div,
    Add,
    Sub,
}

And following function:

fn evaluate(expression: Box<Expr>) -> i32 { // Takes ownership of expression
    match *expression {  // Deref box
        Expr::Number(n) => n,
        Expr::Op(rv, opcode, lv) => match opcode { // Destructure owned expression via move
            // I expect get values rv, opcode, lv owned by this block
            Opcode::Mul => evaluate(rv) * evaluate(lv),
            Opcode::Div => evaluate(rv) / evaluate(lv),
            Opcode::Add => evaluate(rv) + evaluate(lv),
            Opcode::Sub => evaluate(rv) - evaluate(lv),
        },
    }
}

This function doesn’t compile with following error:

error[E0382]: use of collaterally moved value: `(expression:ast::Expr::Op).1`
--> src/main.rs:9:22
|
|         Expr::Op(rv, opcode, lv) => match opcode {
|                  --  ^^^^^^ value used here after move
|                  |
|                  value moved here
|
= note: move occurs because `(expression:ast::Expr::Op).0` has type `Box<ast::Expr>`, which does not implement the `Copy` trait

error[E0382]: use of collaterally moved value: `(expression:ast::Expr::Op).2`
--> src/main.rs:9:30
|
|         Expr::Op(rv, opcode, lv) => match opcode {
|                  --          ^^ value used here after move
|                  |
|                  value moved here
|
= note: move occurs because `(expression:ast::Expr::Op).0` has type `Box<ast::Expr>`, which does not implement the `Copy` trait

I can workaround this issue by passing all expressions via reference. But why cannot I use move semantics to destructure enum?


#2

This seems to be a known limitation. You can work around it by separating the dereference of the Box from the match:

fn evaluate(expression: Box<Expr>) -> i32 {
    let e = *expression;
    match e { ... }
}

The original error has been filed as Issue 30564.


#3

Thank you, that solved my problem.