Borrow container and mutably borrow iterated items at the same time

Consider this example:

struct Item {
    name: String,
}

struct Container {
    name: String,
    items: Vec<Item>,
}
impl Container {
    fn do_something_with_item(&self, item: &mut Item) {
        item.name.push_str(" is in container ");
        item.name.push_str(&self.name);
    }
}

fn main() {
    let mut c = Container {
        name: String::from("flugeldufel"),
        items: vec![
            Item {
                name: String::from("hello"),
            },
            Item {
                name: String::from("world"),
            },
        ],
    };

    for mut item in &mut c.items {
        c.do_something_with_item(&mut item);
        println!("{}", item.name);
    }
}

The error:

error[E0502]: cannot borrow `c` as immutable because it is also borrowed as mutable
  --> src/main.rs:30:9
   |
29 |     for mut item in &mut c.items {
   |                     ------------
   |                     |
   |                     mutable borrow occurs here
   |                     mutable borrow later used here
30 |         c.do_something_with_item(&mut item);
   |         ^ immutable borrow occurs here

If I understand correctly, the method call borrows the whole of c (because of &self) even though it only uses name from it and the iteration borrows c as well (or at least c.items).

What are my options?

  • Change &self method into a static method that takes only the fields it needs
  • Put the items inside RefCell or Mutex, so that you don't need to mutably borrow the whole struct.
  • Split the struct into two: item-holding-struct and item-processing-struct.
2 Likes

Niko from the Rust core team has a very good article on this problem (methods borrowing the entire self when they only need a subset of the fields) and the various solutions to it: http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts

2 Likes