Sorry if this is too basic a question; I'm trying to learn rust and maybe I've hit upon something trivial.
I get this error message for the code below:
error[E0507]: cannot move out of `pet` as enum variant `Cat` which is behind a shared reference
I don't see whose ownership I'm transferring out of "pet", and who else is sharing ownership (of that unknown thing). With some trial-and-error, I realized that it has something to do with the "last" method of Vec, but I couldn't get any more clues beyond that.
Thank you.
use Pet::*;
fn main() {
let mut pets : Vec<Pet> = vec![];
pets.push(Cat(PetInfo{name: "tom".to_string()}));
let pet = pets.last().unwrap();
match pet {
Cat(mut petinfo) => {
petinfo.change_name("tommy".to_string());
}
_ => ()
};
}
enum Pet {
Cat(PetInfo),
Dog(PetInfo)
}
struct PetInfo {
name: String
}
impl PetInfo {
fn change_name(&mut self, new_name: String) {
self.name = new_name;
}
}
Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of `pet` as enum variant `Cat` which is behind a shared reference
--> src/main.rs:9:11
|
9 | match pet {
| ^^^
10 | Cat(mut petinfo) => {
| -----------
| |
| data moved here
| move occurs because `petinfo` has type `PetInfo`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
error: could not compile `playground` due to previous error
The issue here is that last() returns a (shared) reference to the last element of the vector. That is, pet is of type &Pet, not Pet, so you don't have ownership of it. That means you can't change it (because shared references are immutable) or move out of it (because that would leave an invalid element in the vector). Unfortunately, the match there destructures pet moving the petinfo out of it.
Your options here would be to either remove the last element from the vector with pop(), then you can do what you want with it but it would no longer be in the vector (unless you push it back in afterwards), or get a unique reference that allows for mutating in place with last_mut().
Here's an example with last_mut():
use Pet::*;
fn main() {
let mut pets: Vec<Pet> = vec![];
pets.push(Cat(PetInfo {
name: "tom".to_string(),
}));
let pet = pets.last_mut().unwrap();
match pet {
Cat(petinfo) => {
petinfo.change_name("tommy".to_string());
}
_ => (),
};
println!("{:?}", pets);
}
#[derive(Debug)]
enum Pet {
Cat(PetInfo),
Dog(PetInfo),
}
#[derive(Debug)]
struct PetInfo {
name: String,
}
impl PetInfo {
fn change_name(&mut self, new_name: String) {
self.name = new_name;
}
}
ref and ref mut are used to take references when matching against a pattern. The reason you can omit them is due to something called "match ergonomics" that was added to make writing match expressions easier, where it will assume you want a reference out if you give a reference in. Adding ref or mut to your match explicitly disables match ergonomics.