2

I'm new to Rust. Below is my testing.

#[derive(Debug)]
enum Food {
    Cake,
    Pizza,
    Salad,
}

#[derive(Debug)]
struct Bag {
    food: Food
}

fn main() {
    let bag = Bag { food: Food::Cake };
    match bag.food {
        Food::Cake => println!("I got cake"),
        x => println!("I got {:?}", x)
    }

    println!("{:?}", bag);
}

When I run it, I got an error.

error[E0382]: borrow of moved value: `bag`
  --> src\main.rs:20:22
   |
17 |         x => println!("I got {:?}", x)
   |         - value moved here
...
20 |     println!("{:?}", bag);
   |                      ^^^ value borrowed here after partial move
   |
   = note: move occurs because `bag.food` has type `Food`, which does not implement the `Copy` trait

It's clear that bag.food will not match x arm in the code. Why a move happens there?

Just a learner
  • 26,690
  • 50
  • 155
  • 234
  • 3
    Its telling you on the "note:" – William Bright Dec 06 '19 at 14:40
  • This has nothing to do with the arms, when you match something, it is moved (or copied, if possible). – Boiethios Dec 06 '19 at 14:41
  • Either way you borrowing the `x` there because you have to in order to ensure that nothing changes down the road of the program. This prevents weird things from happening like null pointers – William Bright Dec 06 '19 at 14:42
  • Just to make sure we understand your question correctly: Why do you think "it's clear that `bag.food` will not match `x` arm in the code"? Because of the specific value you assigned to `bag.food`, or for some other reason? – Sven Marnach Dec 06 '19 at 14:55
  • @SvenMarnach Because of the specific value assigned, which is `Food::Cake`. – Just a learner Dec 06 '19 at 14:56

1 Answers1

4

It doesn't matter that the x branch is not taken at runtime because whether a match takes ownership of its argument does not depend on which branch of the match will actually be taken. Much like in this code (also see this followup question):

let foo = "blargh".to_owned();
if false {
    let _bar = foo;
}
println!("{}", foo); // error[E0382]: borrow of moved value: `foo`

foo is never actually moved, but that doesn't make any difference to whether it is valid after the if (it isn't).

The match in the question takes ownership of bag.food (invalidating bag) because it has a branch that takes ownership. If you want that particular branch to not take ownership, you can use a ref pattern to borrow instead:

match bag.food {
    Food::Cake => println!("I got cake"),
    ref x => println!("I got {:?}", x)
}

Alternatively, since Rust 1.26 the compiler knows how to bind value patterns (such as Food::Cake) to references (such as &bag.food), so you can write:

match &bag.food {
    Food::Cake => println!("I got cake"),
    x => println!("I got {:?}", x)
}

In this case Food::Cake matches the value, but x matches and is bound to the reference, so it does the same thing as the previous snippet with ref. (You may see this referred to as "default binding modes" or "match ergonomics".)

trent
  • 25,033
  • 7
  • 51
  • 90