haskeller
haskeller

Reputation: 151

Why is an iterator moved in this function?

I want to get a function to accept two Iterators and a callback and process as follows:

fn for_xy<I: Iterator<Item = usize>, F>(ix: I, iy: I, f: F)
where
    I: Iterator<Item = usize>,
    F: FnMut(usize, usize) -> (),
{
    for x in ix {
        for y in iy {
            f(x, y);
        }
    }
}

but I get this error:

error[E0382]: use of moved value: `iy`
   --> src/lib.rs:7:18
    |
1   | fn for_xy<I: Iterator<Item = usize>, F>(ix: I, iy: I, f: F)
    |                                                -- move occurs because `iy` has type `I`, which does not implement the `Copy` trait
...
7   |         for y in iy {
    |                  ^^ `iy` moved due to this implicit call to `.into_iter()`, in previous iteration of loop
    |
note: this function takes ownership of the receiver `self`, which moves `iy`
help: consider borrowing to avoid moving into the for loop
    |
7   |         for y in &iy {
    |                  ^^^
help: consider further restricting this bound
    |
3   |     I: Iterator<Item = usize> + Copy,
    |                               ^^^^^^

How can I correct this program?

Upvotes: 4

Views: 1813

Answers (1)

Lucretiel
Lucretiel

Reputation: 3344

When you run a for loop, the iterable argument you pass is moved into the for loop; after iteration completes, the iterator is dropped. If you want to make the iterator reusable, the typical way is to add a Clone bound. Additionally, in principle you're going to have the same problem (in reverse) with you x items from the ix iterator: because you're reusing them with each iteration of the iy loop, you need to ensure they can be cloned or copied. This problem doesn't happen with usize, which is copyable, but a more generic version will need to include a Clone or Copy bound on ix::Item:

fn for_xy<X, Y, F>(ix: X, iy: Y, f: F)
where
    X: Iterator,
    X::Item: Clone,
    Y: Iterator + Clone,
    F: FnMut(X::Item, Y::Item)
) -> {
    for x in ix {
        for y in iy.clone() {
            f(x.clone(), y)
        }
    }
}

In practice, most iterators you encounter in Rust are cheaply cloneable. The colletion iterators (such as Vec::iter, HashMap::iter) contain simple references to the underlying collection, and most iterator adapters (map, filter, etc) are cheaply cloneable so long as the iterator being adapted is as well.

Upvotes: 3

Related Questions