Return mutable instance of entry of an object

Viewed 74

I want to understand if it is allowed to get a mutable reference to an entry of a vector inside a struct in Rust.

I have a vector in a struct and I want to return a reference to one of its indexed fields.

Sample code:


#[derive(Debug)]
pub struct Definition {
    v1: Vec<(String, i32)>,
}

impl Definition {
    fn new() -> Self {
        Self {
            v1: Vec::new(),
        }
    }

    fn insert_some_values(&mut self) {
        self.v1.push(("V1".to_string(), 0));
        self.v1.push(("V2".to_string(), 1));
        self.v1.push(("V3".to_string(), 2));
        self.v1.push(("V4".to_string(), 3));
    }

    fn search(&mut self, st: String) -> Result<&mut (String, i32), u32> {
        for i in &self.v1 {
            if st.eq(&i.0) {
                return Ok(I); // Is this supported in someway?
            }
        }
        return Err(0);
    }
}

fn main() {
    let mut def = Definition::new();
    def.insert_some_values();
    let res = def.search("V2".to_string());
    println!("{:?}", res);
}

Obviously, it errors in compiling only:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:23:27
   |
23 |                 return Ok(i);
   |                           ^ types differ in mutability
   |
   = note: expected mutable reference `&mut (String, i32)`
                      found reference `&(String, i32)`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

But I want to understand if there is some way to support it?

Most likely, it is not supported since in this way, it is essentially losing part ownership of the field to the caller and that is not allowed. Perhaps only partial ownership of the individual items in a struct is allowed but not within the object.

But if it is supported, can someone please help?

Thanks!

Playground

1 Answers

What you're trying to do is fine — you just missed one point:

    fn search(&mut self, st: String) -> Result<&mut (String, i32), u32> {
        for i in &mut self.v1 {   // HERE
            if st.eq(&i.0) {
                return Ok(i);
            }
        }
        return Err(0);
    }

The loop must be written as for i in &mut self.v1 (or, equivalently, for i in self.v1.iter_mut() so that the is you iterate over are &mut references, not & references. With this change your code will compile and run.


Some additional suggestions:

  • Don't use unnecessary returns.
  • Use == instead of .eq().
  • Don't return an error value that conveys no information — use Option instead.
  • Use &str instead of String so the caller is not obligated to pass in a string that will then be deallocated.
    fn search(&mut self, st: &str) -> Option<&mut (String, i32)> {
        for i in self.v1.iter_mut() {
            if i.0.as_str() == st {
                return Some(i);
            }
        }
        None
    }

You can also, if you choose, write the function using iterator methods alone. This is the same as the loop but might be more convenient and/or readable — not necessarily in this case, but in slightly more complex ones:

    fn search(&mut self, st: &str) -> Option<&mut (String, i32)> {
        self.v1.iter_mut().filter(|i| i.0.as_str() == st).next()
    }
Related