Assuming I have those structs (no fancy pointer, everything is copyable)
#[derive(Debug, Copy, Clone)]
struct Item {
id: u32,
pub value: u32
}
I can create a struct to hold a vec of such items:
#[derive(Debug)]
struct Container {
next_id: u32,
items: Vec<Item>
}
It’s fairly easy to add elements, and change them if you know the exact position:
impl Container {
fn new() -> Container {
Container {
next_id: 0,
items: vec!()
}
}
fn add(&mut self) -> u32 {
let id = self.next_id;
self.items.push(Item {
id: self.next_id,
value: 0
});
self.next_id = self.next_id + 1;
id
}
fn lookup(&mut self, id: u32) -> Option<&Item> {
self.items.iter().find(|item| item.id == id)
}
fn change_first(&mut self, new_value: u32) {
if self.items.len() < 0 {
panic!("No item to change");
}
self.items[0].value = new_value;
}
}
This makes such a test pass:
#[test]
fn can_change_first() {
let mut c = Container::new();
assert_eq!(c.add(), 0);
c.change_first(22);
assert_eq!(c.lookup(0).unwrap().value, 22);
}
However, suppose I want to lookup and element and find it. I don’t want to expose the
items
vec directly, but propose an API to do so:
#[test]
fn can_lookup_and_change() {
let mut c = Container::new();
assert_eq!(c.add(), 0);
c.change_at(0, 22);
assert_eq!(c.lookup(0).unwrap().value, 22);
}
The naïve implementation of change_at
would be:
fn change_at(&mut self, id: u32, new_value: u32) {
let lookup = self.lookup(id);
match lookup {
Some(item) => {
item.value = new_value;
}
None => {
// ...
}
}
}
However, the compiler does not accept that, giving the error:
Compiling web v0.1.0 (/home/phtrivier/perso/prj/wirth/dom-web)
error[E0594]: cannot assign to `item.value`, which is behind a `&` reference
--> redacted/src/lib.rs:170:17
|
169 | Some(item) => {
| ---- consider changing this binding's type to be: `&mut Item`
170 | item.value = new_value;
| ^^^^^^^^^^^^^^^^^^^^^^ `item` is a `&` reference, so the data it refers to cannot be written
For more information about this error, try `rustc --explain E0594`.
I don’t understand if the error is on the lookup
side (is there a different way to lookup up items that will make them mut ?) or on the change_at
side (is this the right way to pattern match ?)
>Solution :
You can add a lookup_mut
method. This would replace iter
with iter_mut
.
fn lookup_mut(&mut self, id: u32) -> Option<&mut Item> {
self.items.iter_mut().find(|item| item.id == id)
}
And then use that when you need a mutable reference.
let lookup = self.lookup_mut(id);