2

Consider the program:

fn main() {
    let mut nums = vec![1, 3];    

    let mut counter = 0;
    for mut p in nums.into_iter().cycle() {
        println!("{}", p);
        p += 1;
        if p > 10 {
            break;
        }

        counter += 1;
        if counter > 1000 {
            println!("ERROR");
            break;
        }
    }
}

I expected this to print 1, 3, 2, 4, 3, 5, ... until it got to 10 and then halt. Instead, I got a warning:

warning: variable does not need to be mutable
 --> src/main.rs:2:9
  |
2 |     let mut nums = vec![1, 2, 3];
  |         ----^^^^
  |         |
  |         help: remove this `mut`
  |

And then an infinite loop (well, only finite because I added the counter in there). Why is this an infinite loop?


The behavior I wanted can also be written this way:

for idx in (0..nums.len()).cycle() {
    let p = &mut nums[idx];
    println!("{}", p);
    *p += 1;
    if *p > 10 {
        break;
    }
}

That works - but I don't want to iterate over the indices, I want to directly iterate over the values. Is there a way to write what I want?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    you want `iter_mut()` here. cycle clone iterator to you end up by cloning your orignal vec for ever. And actually iter mut will probably not compile – Stargateur Dec 09 '19 at 17:45
  • btw is this for the day7 in advents of code ? – Stargateur Dec 09 '19 at 17:49
  • @Stargateur Yes, I do want `iter_mut()`, you're right. And yes, that doesn't compile. Why not? And... perhaps... :-) ... – Barry Dec 09 '19 at 17:53
  • 3
    because you can't have more than one mut reference to the same object so `cycle()` can't clone the iterator. – Stargateur Dec 09 '19 at 17:58

2 Answers2

4

The cycle() iterator adaptor works by cloning each of the values in the iterator so that they may be traversed multiple times. As this was applied on top of nums.into_iter(), the vector was fully consumed and p += 1 does not modify the vector retained in the cycle adaptor. The program would not end because no progress was being made.

You would want an iterator of mutable references, so that the vector is modified rather than consumed. But cycling an iterator of mutable references is not permitted because the adaptor needs to clone those references, which is illegal. A streaming iterator, which can handle incoming items in an exclusive fashion, would probably allow you to do this, but such an API is not usable without generic associated types (GATs).

One possible solution is not to use .cycle(): create an outer loop to traverse the vector multiple times and break explicitly on that loop once the intended condition arrives.

let mut nums = vec![1, 3];    

let mut counter = 0;

'outer: loop {
    for p in nums.iter_mut() {
        println!("{}", p);
        *p += 1;
        if *p > 10 {
            break 'outer;
        }

        counter += 1;
        if counter > 1000 {
            println!("ERROR");
            break 'outer;
        }
    }
}

Playground

E_net4
  • 27,810
  • 13
  • 101
  • 139
3

As mentioned by Stargateur in the comments, Vec::into_iter creates an iterator of the values in the vector. Iterator::cycle then clones the iterator each time it is exhausted:

fn cycle(self) -> Cycle<Self>
where
    Self: Clone, 

Instead of stopping at None, the iterator will instead start again, from the beginning. After iterating again, it will start at the beginning again. And again. And again. Forever.

Your code is effectively doing:

loop {
    let mut p = 1;
    p += 1;
}

This is also stated, indirectly, in two ways:

  1. The unneeded mut warning from the compiler: the vector itself is never modified.

  2. Your working example has a mutable reference that needs to be dereferenced: *p += 1. Your non-working version has a value.

You'd want to use iter_mut instead of into_iter. This will give you mutable references to values in the vector instead of consuming it. However, you cannot easily use cycle with iter_mut as mutable references cannot be copied; doing so could trivially lead to mutable aliasing, which is forbidden by Rust's rules of references.

The correct workaround is to use indices, as you have done.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366