Why does Iterator::take_while take ownership of the iterator?

All the iterator adapters take the original iterator by value for efficiency’s sake. Additionally, taking ownership of the original iterator avoids having to deal with lifetimes when it isn’t necessary.

If you wish to retain access to the original iterator, you can use by_ref. This introduces one level of indirection, but the programmer chooses to opt into the extra work when the feature is needed:

fn main() {
    let v = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut i1 = v.iter();
    for z in i1.by_ref().take_while(|&&v| v < 4) {
        //     ^^^^^^^^^
        println!("Take While: {}", z);
    }

    for z in i1 {
        println!("Rest: {}", z);
    }
}

Has the output

Take While: 1
Take While: 2
Take While: 3
Rest: 5
Rest: 6
Rest: 7
Rest: 8

Iterator::by_ref works because there’s an implementation of Iterator for any mutable reference to an iterator:

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

This means that you can also take a mutable reference. The parenthesis are needed for precedence:

for z in (&mut i1).take_while(|&&v| v < 4)

Did you note that 4 was missing? That’s because once take_while picks a value and decides to not use it, there’s nowhere for it to “put it back”. Putting it back would require opting into more storage and slowness than is always needed.

I’ve used the itertools crate to handle cases like this, specifically take_while_ref:

use itertools::Itertools; // 0.9.0

fn main() {
    let v = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut i1 = v.iter();
    for z in i1.take_while_ref(|&&v| v < 4) {
        //     ^^^^^^^^^^^^^^^
        println!("Take While: {}", z);
    }

    for z in i1 {
        println!("Rest: {}", z);
    }
}
Take While: 1
Take While: 2
Take While: 3
Rest: 4
Rest: 5
Rest: 6
Rest: 7
Rest: 8

Leave a Comment