Why 'take' iterator is a consuming iterator?

I wrote (the one I think neat) piece of code, but I can't compile, because take is consuming elements iterator, instead of borrowing it.

    let mut layer_sizes = Vec::new();
    let mut layer_size = 1;
    let mut elements = source.iter().chain(std::iter::repeat(&None));
    while layer_size > 0{
        layer_sizes.push(layer_size);
        layer_size = *(&mut elements.take(layer_size).filter(|x| x.is_some()).count()) * 2;
    }

I wanted to 'take N elements from iterator' in a loop. I thought take is a perfect match for that, but it's not. Either I miss something here (which I do, I suppose), or it's less useful than I hoped.

Try (&mut elements).take(…), so that it consumes the new mutable reference instead of the original iterator.

3 Likes

Also see by_ref method (equivalent to &mut iter).

6 Likes

It's useful as a consuming method, too. For example, if you want the first N items of an iterator and ignore the rest, then .take(N) is what you want as-is.

In fact, this is a more typical use case than "take and then continue iterating over the original iterator".

One pitfall of the non-consuming case is that if the (&mut iter).take(n) iterator is not run to completion, then less than n elements are removed from the original iterator.


Another important design decision here is that while a consuming .take(n) method can be applied to the non-consuming case (by passing a mutable reference to an iterator), the reverse is not true. You cannot obtain an owned iterator only returning the first n items of some other iterator, if you only have a &mut self version of .take(n) (unless you start working with some third-party crate solution to create self-referencing data structures).

5 Likes