rustownership

Why doesn't (&mut iter).take(n) take ownership of iter?


https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=27451eb0a114417afcea5b6c53a92ff2

In this code:

fn main() {
    let my_vec = vec![1,2,3,4,5,6,7,8];
    let mut pair: Vec<Vec<u8>> = vec![vec![], vec![]];
    
    let mut my_vec_iter = my_vec.into_iter();
    // pair.iter_mut().for_each(|v| v.append(&mut my_vec_iter.take(4).collect()));
    // error[E0507]: cannot move out of `my_vec_iter`, a captured variable in an `FnMut` closure
    
    let my_ref = &mut my_vec_iter;
    pair.iter_mut().for_each(|v| v.append(&mut my_ref.take(4).collect()));
    
    println!("{:?}", pair);
}

The commented-out line doesn't compile, because (as I understand it) take() takes ownership of its receiver and you can't do that with a captured variable.

But the alternative version, where the captured variable is &mut my_vec_iter, does work, and I don't understand what's going on. I haven't been able to find a version of take(&mut self), so presumably it's still calling take(self)?

Explain like I'm 5 - how come taking an explicit mutable reference stops take taking ownership? Or, if I've misunderstood something, what is going on?


Solution

  • This works because Iterator is also implemented for references with this impl.

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

    Iterator::take always takes self, but you can use either I or &mut I as the type of self.

    There's also a convenience method on Iterator just for obtaining a &mut self reference, so you don't need to make another binding or contend with operator precedence: Iterator::by_ref.

    // these are identical
    
    my_vec_iter.by_ref().take(4)
    
    (&mut my_vec_iter).take(4)
    

    You should also use extend when appending from an iterator. This avoids allocating a temporary Vec.

    pair.iter_mut().for_each(|v| v.extend(my_vec_iter.by_ref().take(4)));