Problem with "Cannot mutably borrow immutable field" inside a for loop


#1

Hi,
I’m having some trouble with the following code:

(also here: https://play.rust-lang.org/?gist=30c2dbc8de984a109f02e98fc845b10f&version=stable, although it doesn’t compile since it depends on the ego-tree crate).

extern crate ego_tree;

use ego_tree::{Tree, NodeId};
use std::path::{PathBuf};

#[derive(Debug, PartialEq)]
pub struct Folder {
    pub path: PathBuf,
    pub files: Vec<File>,
}

#[derive(Debug, PartialEq)]
pub struct File {
    pub path: PathBuf,
    pub content: Vec<u8>,
}

fn testtest(tree: &mut Tree<Folder>, curNodeId: NodeId<Folder>) {
    let mut folder = tree.get_mut(curNodeId);
    
    // This works, so .path *is* mutable
    folder.value().files[0].path = PathBuf::new();
    
    for mut file in &folder.value().files {
        // This doesn't work, and claims path isn't mutable!  
        // It gives me:
        // error[E0594]: cannot assign to immutable field `file.path`
        file.path = PathBuf::new();
    }
}

Why is the path field immutable here? It’s obviously mutable, as the line above shows which assigns it an empty PathBuf. But it’s suddenly immutable inside the for loop.


#2

It’s because you are immutably borrowing the iterator: &folder.value().files. Change it to a mutable borrow: &mut folder.value().files.

Here’s a smaller repro case that has the same problem: https://play.rust-lang.org/?gist=b7f64a72aed8383634a14929eacb5a4e&version=stable Changing the borrow to be mutable solves the problem and the program runs, replacing all of the paths with “abc”.


#3

To add to @parasyte’s reply, let mut x = &Foo just means that you can change which Foo x points to, but won’t let you change the value behind the reference (which requires x to be a &mut Foo.

The way your assumption was stated applies to when x is a binding that owns the value - then you can mutate the value. But references are different.


#4

Many thanks, @parasyte and @vitalyd! For some reason it seemed unnatural to me that &mut could be used on the iterator of a for loop. I’m still not too sure what exactly the for loop desugars to…

A few questions, please excuse me if they are beginner’s questions. I’ve been away from Rust for a while.

  1. Isn’t the iterator a temporary returned by invoking for on the Vec? I guess it isn’t because then it wouldn’t make sense to take a reference to it, let alone a mutable one. The for syntax suggests it is, but apparently it’s saved somewhere (an anonymous hidden variable perhaps, created by the compiler?)

  2. Thinking about it, what does it mean exactly a mutable iterator? Looking at the Vec documentation, is this the IterMut type?


#5

That usually means an iterator that returns mutable references, which is IterMut for the Vec.


#6

Many thanks for that great blog post, it really helped! That whole blog is very interesting too.

So… if I’m understanding this correctly, since for is actually desugared into a call to into_iter followed by usage of the returned iterator, the thing that is being borrowed is not the iterator (as @parasyte suggested above) but the vec itself, in the call to into_iter.


#7

That’s correct. The iterator itself is borrowed mutably as well so that next() can be called on it - that’s irrespective of what type of iterator you have (ie one that returns immutable or mutable references). The &mut folder.value().files borrows the Vec mutably, and there’s a ‘IntoIteratorimpl for it that returns anIterMut` struct, which in turn yields mutable references. So the type of borrow of the Vec (mutable or immutable) dictates the type of iterator you get (yielding mutable or immutable references, respectively).