rust - Preventing move semantics during pattern matching -
i have silly example here, demonstrate issue i'm running library , 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 < 80 => { announce(p); } (_, a) if >= 80 => { println!("you're old care."); } _ => { println!("you're not nice!") } } } fn announce(p: person) { println!("your name {}. {:?}.", p.name, p.choice); } fn main() { let p = person { name: "bob".to_string(), age: 20, choice: choices::good }; find(p); }
now issue seems during pattern matching, move semantics kick in , take ownership on inner struct (thing) in person.
when go move person on next method, can't because it's been 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 has type `choices`, non-copyable src/main.rs:15 match (p.choice, p.age) { ^~~~~~~~ error: aborting due previous error not compile `match`.
my gut says need rust stop moving value using reference or borrow of kind. in case could change method signature borrow, libraries aren't able that. (i trying deal hyper in case...)
is there way match
use references during matching instead of moving values? thank you!
why?
when make tuple
(p.choice, p.age)
you memcpy
both p.choice
, p.age
person
.
it's ok p.age
because it's copy
type - can continue using old value after memcpy
ing it.
p.choices
of type choices
not copy
. means memcpy
treated "move", old value not usable. means p
in invalid state, can't call announce
on it.
solution #1
since choices
trivial enum
, can #[derive(copy, clone)]
. means allowed continue using old p.choices
.
if can safely make choices
clone
, you'd have clone
in match
instead.
solution #2
you can take p.choices
reference:
match (&p.choice, p.age) { (&choices::good, a) if < 80 => { announce(p); } ... }
this works because &choices::good
exact match borrow can relinquished. if had instead
match (&p.choice, p.age) { (&x, a) if < 80 => { announce(p); } ... }
the borrow still active , move when calling announce(p)
fail - move invalidate active borrowed variable.
notes
you're doing awful lot of moving here - passing few references lot more flexible! there's no reason announce
consume person
- needs @ bit. taking value when take reference advisable small copy
types.
note having announce
take reference means match
allowed holding on references inside p
, makes more applicable.
to_string
use non-string objects. into
, to_owned
faster , into
lot 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 < 80 => { announce(p); } (_, a) if >= 80 => { println!("you're old care."); } _ => { println!("you're not nice!") } } } fn announce(p: &person) { println!("your name {}. {:?}.", p.name, p.choice); } fn main() { let p = person { name: "bob".into(), age: 20, choice: choices::good }; find(&p); }
Comments
Post a Comment