Prevent semantics of movement during pattern matching

I have a stupid example, just to demonstrate the problem that I came across with another library and pattern matching.

struct Person { name: String, age: i32, choice: Choices } #[derive(Debug)] enum Choices { Good, Neutral, Evil } fn find(p: Person) { match (p.choice, p.age) { (Choices::Good, a) if a < 80 => { announce(p); } (_, a) if a >= 80 => { println!("You're too old to care."); } _ => { println!("You're not very nice!") } } } fn announce(p: Person) { println!("Your name is {}. You are {:?}.", p.name, p.choice); } fn main() { let p = Person { name: "Bob".to_string(), age: 20, choice: Choices::Good }; find(p); } 

Now the problem is that during pattern matching, the movement of semantics will begin and take ownership of the internal structure (Thing) in my Person.

When I move on to the next method, I cannot, because it is partially moved.

 Compiling match v0.1.0 (file:///home/jocull/Documents/Projects/Rust/learn/match) src/main.rs:17:13: 17:14 error: use of partially moved value: `p` src/main.rs:17 announce(p); ^ src/main.rs:15:9: 15:17 note: `p.choice` moved here because it has type `Choices`, which is non-copyable src/main.rs:15 match (p.choice, p.age) { ^~~~~~~~ error: aborting due to previous error Could not compile `match`. 

My gut says I need to get Rust to stop moving the value using a link or some kind of loan. In this case, I could change my method signature for borrowing, but with some libraries you cannot always do this. (In this case, I'm trying to solve hyper ...)

Is there a way to get match use links during matching instead of moving values? Thanks!

+5
source share
2 answers

Why?

When you make a tuple

 (p.choice, p.age) 

you memcpy both p.choice and p.age from your Person .

This is normal for p.age because it is of type Copy - you can continue to use the old value after memcpy ing from it.

p.choices is of type Choices , which is not Copy . This means that memcpy treated as a "move", so the old value is not used. This means that p is in an invalid state, so you cannot invoke announce on it.

Solution No. 1

Since Choices is a trivial enum , you can simply #[derive(Copy, Clone)] . This means that you are allowed to continue using the old p.choices .

If you can safely make Choices Clone , then you will have to Clone it in match .

Decision No. 2

You can take p.choices from the link:

 match (&p.choice, p.age) { (&Choices::Good, a) if a < 80 => { announce(p); } ... } 

This only works because &Choices::Good is an exact match, so borrowing may be refused. If you used

 match (&p.choice, p.age) { (&x, a) if a < 80 => { announce(p); } ... } 

the lesson will still be active, and therefore the transition when calling announce(p) will end with an error - moving will invalidate the active borrowed variable.

Notes

You are moving a lot here - submitting multiple links is much more flexible! There is no reason for announce consume Person - he just needs to take a look at it. Accepting when you can take a link is only recommended for small types of Copy .

Note that if announce take the link means that match can also be held on links inside p , which makes it more widely applicable.

to_string is mainly used for non-string objects. into and to_owned faster, and into also much shorter.

 struct Person { name: String, age: i32, choice: Choices } #[derive(Copy, Clone, Debug)] enum Choices { Good, Neutral, Evil } fn find(p: &Person) { match (p.choice, p.age) { (Choices::Good, a) if a < 80 => { announce(p); } (_, a) if a >= 80 => { println!("You're too old to care."); } _ => { println!("You're not very nice!") } } } fn announce(p: &Person) { println!("Your name is {}. You are {:?}.", p.name, p.choice); } fn main() { let p = Person { name: "Bob".into(), age: 20, choice: Choices::Good }; find(&p); } 
+10
source

So, I will try again with a new code change :)

In your current code, if you use borrowing instead of moving in a match, it works.

p.age does not need that because the primitive type and primitive types implement Copy trait

But the choice does not realize the trait of the copy, and therefore they move in the match. Because of what they will not be available when you call announce()

 match (&p.choice, p.age) { (&Choices::Good, a) if a < 80 => { announce(p); } ... } 

It receives an error of partial movement error. Perhaps this is because you have moved the choice in the match. But choice is part of Man, so he partially moves.

I don’t have enough knowledge about Rust to really explain why it works, so if you can add something useful, do

0
source

All Articles