Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Can you lookup an element of a vector and change it?

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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);
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading