Search code examples
rustiteratormove-semantics

How can I take a number of values from an iterator inside a for loop?


I have an infinite iterator and I want to print different slices from the iterator.

So, if I call extract(8,&[1,2,3,4]), in console i want to see/get:

1
2 3
4 1 2
3 4 1 2
3 4 1 2 3
4 1 2 3 4 1
2 3 4 1 2 3 4

My attemp:

fn extract(n: usize, seq: &[u16]) {
    let mut iterator = seq.iter().cycle();

    for it in 1..n {
        iterator.take(it).for_each(|x| print!("{} ", x)); // <- Problem is here
        print!("\n");
    }
}

When I compile:

warning: variable does not need to be mutable
 --> src/lib.rs:2:9
  |
2 |     let mut iterator = seq.iter().cycle();
  |         ----^^^^^^^^
  |         |
  |         help: remove this `mut`
  |
  = note: `#[warn(unused_mut)]` on by default

error[E0382]: use of moved value: `iterator`
 --> src/lib.rs:5:9
  |
2 |     let mut iterator = seq.iter().cycle();
  |         ------------ move occurs because `iterator` has type `std::iter::Cycle<std::slice::Iter<'_, u16>>`, which does not implement the `Copy` trait
...
5 |         iterator.take(it).for_each(|x| println!("{} ", x));
  |         ^^^^^^^^ value moved here, in previous iteration of loop

I understand that iterator changes its value in each loop iteration. That is why I marked as mutated var. But somehow, iterator var is used as argument of a function (which I can't see) and the iterator is moved. So in the next loop iteration, iterator var is out of scope and I get the moved value error.

To understand better my code:

  • Where is my iterator variable moving to?

  • How can I use the iterator inside the loop without this problem?


Solution

  • As others wrote, take consumes your iterator. To avoid this, you can use by_ref which wraps the original iterator and can be consumed without consuming the original iterator

    fn extract(n: usize, seq: &[u16]) {
        let mut iterator = seq.iter().cycle();
    
        for it in 1..n {
            iterator.by_ref().take(it).for_each(|x| println!("{} ", x));
            print!("\n");
        }
    }