Search code examples
rustmoveborrow-checker

Rust `borrow_mut`, `borrow_mut` and move


My code is

struct Test<Source>
where
    Source: Iterator<Item = char>,
{
    source: Source,
}

impl<Source: Iterator<Item = char>> Test<Source> {
    fn read(&mut self) {
        while let Some(item) = self.source.next() {
            match item {
                '#' => self.read_head(),
                _ => {}
            }
            println!("Handled char {}", item);
        }
    }

    fn read_head(&mut self) {
        println!("Start read heading");
        let source = self.source.borrow_mut();
        let level = source.take_while(|&char| char == '#').count();
        println!("Done, level is {}", level);
    }
}

fn main() {
    let str = "##### Hello World".to_string();
    let str = str.chars().into_iter();
    let mut test = Test { source: str };
    test.read();
}

It works good. But in this line:

let source = self.source.borrow_mut();

If changes borrow_mut to borrow, will produce the error:

error[E0507]: cannot move out of `*source` which is behind a shared reference
  --> src/main.rs:24:21
   |
24 |         let level = source.take_while(|&char| char == '#').count();
   |                     ^^^^^^ move occurs because `*source` has type `Source`, which does not implement the `Copy` trait

So why borrow_mut works but borrow not. I don't have a clear understanding about relation between move and borrow.


Solution

  • So why borrow_mut works but borrow not.

    Because there is an implementation of Iterator for exclusive references to iterators:

    impl<'_, I> Iterator for &'_ mut I where I: Iterator + ?Sized
    

    This means an exclusive reference to an Iterator is itself an iterator, and can be used "as is" (which incidentally is why Iterator::by_ref is a thing, and useful).

    I don't have a clear understanding about relation between move and borrow.

    There isn't one in and of itself, it's just that in this case a mutable reference to an iterator is also an iterator (in its own right), which means it fulfills take_while's requirements to take an iterator by value:

    pub fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P> where P: FnMut(&Self::Item) -> bool
    

    a shared reference is not so, and thus triggers the error you see.