Unable to iter_mut#cycle

It looks like the iter_mut is not capable of doing cycle. Why is this and does anybody know a smart workaround? Unfortunately the Cycle::new constructor that the normal iterator uses is private :frowning:.

let mut a = vec![1, 2, 3];
let it = a.iter_mut().cycle();
error[E0277]: the trait bound `std::slice::IterMut<'_, {integer}>: std::clone::Clone` is not satisfied
  --> src/mod.rs:52:31
   |
52 |         let it = a.iter_mut().cycle();
   |                               ^^^^^ the trait `std::clone::Clone` is not implemented for `std::slice::IterMut<'_, {integer}>`

To see why this isn't allowed, consider what would happen if it were allowed and you were to do something like this:

let b = it.take(4).collect::<Vec<&mut u32>>()

Then b[0] and b[3] are the same &mut reference, which shouldn't happen since those are supposed to be unique. It is thus disallowed to cycle an iterator of unique references (or indeed anything that isn't Clone).

As to how to fix it, that depends on what you were trying to do in the first place. Why do you need to reference the same thing multiple times?

3 Likes

Indeed the collect would not be possible, but maybe that is the problem, not allowing a cycling mutable iterator.

The case I am describing is perfectly valid:

        let round_robin = self.partitions.iter_mut();


        it.zip(round_robin)
            .for_each(|(element, partition)| {
                partition.push(element);
            });

This is a very basic use case in my opinion. Maybe a MutableCycle should be created. This could offer an API which ensures the passed reference lifetimes do not persist long enough to reach the next possible cycle(so it can not be stored) no? In other words if I offer a for_each API, I have to ensure the passed element's lifetime will end after calling the function offered as parameter.

My use case can be done very easily with a .enumerate but I generally prefer the functional approach for readability and simplicity:

let partition_len = self.partitions.len();
        it.enumerate()
            .for_each(|(index, element)| {
                self.partitions[index % partition_len].push(element);
            });

The Iterator trait has the guarantee of being compatible with collect no matter whether you use it or not.

But you should be able to implement such cycle on top of StreamingIterator.

3 Likes

Thank you, this seems like a viable alternative, but I think I will resort to the std for now.

Don't blame collect -- that's just the extreme of having multiple items alive at once, all of them. But even the basic iterator API allows multiple items alive:

let x = iter.next()?;
let y = iter.next()?;

These two must not alias!

StreamingIterator is nice, but note that it doesn't have methods like min and max, since it can't hold two items at once to make such a comparison.

5 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.