Losing mutability? Turning error[E0596] cannot borrow as mutable into error[E0308] mismatched types does not help

I've a shallow struct nesting with an inner and an outer. I implemented put-methods that shall modify the inner via the outer struct. Both methods take a mutable &self as first parameter. The outer does not compile and I got stuck with error[E0596]: cannot borrow **ias mutable, as it is behind a& reference

use std::collections::BTreeSet;

#[derive(Eq, Ord, PartialEq, PartialOrd)]
struct Inner { values: Vec<String> }
impl Inner {
    fn new() -> Self { Self { values: Vec::new() } }
    fn put(&mut self, val: &str) -> usize {
        self.values.push(val.to_string());
        self.values.len()
    }
}

struct Outer { container: BTreeSet<Inner> }
impl Outer {
    fn new() -> Self { Self { container: BTreeSet::new() } }
    fn put(&mut self, val: &str) -> usize {
        let i = &mut self.container.first().unwrap();
        i.put(&val.to_string())
    }
}

The retrieval of i is as explicit as this post suggests but that does not help.

Usually the compiler gives helpful hints but this time I am lost. Both methods get the underlying struct mutable. i is mutable and enclosed by an owned o so what is wrong here?

I can trade this error in for error[[E0308](https://doc.rust-lang.org/error_codes/E0308.html)]: mismatched types by making i's retrieval more explicit with let i: &mut Inner = self.container.first().unwrap(); but that doesn't help. I assume the expression &mut self.container.first().unwrap(); does not return a mutable owned Inner but that's what should happen.

What am I missing?

the error is caused by BTreeSet::first(), which returns an Option<&T>, note it's a & reference, not &mut reference.

Set<T> does not return &mut T, it's the same case for Map<Key, Value>, which does not return &mut Key. because otherwise, the user can modify the key, which could change the b-tree node position, or hash bucket, of the entry.

to modify a Set, you must pop the element from the container first, then modify it, and finally insert it back into the container. e.g.

impl Outer {
    fn put(&mut self, val: &str) -> usize {
        let mut i = self.container.pop_first().unwrap();
        i.put(val.to_string());
        self.container.insert(i);
    }
}
1 Like

Incidentally this creates a String then drops it after the call to put (which creates its own String). A shallow fix is to just pass in the &str you already have. A better fix IMO is for the methods to take what they need, a String.