When accepting an IntoIterator that you have to iterate over multiple times, where should you put the Clone bound?
fn use_iterable_twice<'a, I>(it: I)
where
I: IntoIterator<Item = &'a str>,
{
for s in it {
println!("{s}");
}
// will error use of moved value
// for s in it {
// println!("{s}");
// }
}
Two approaches work
fn use_iterable_twice<'a, I>(it: I)
where
I: IntoIterator<Item = &'a str>,
I::IntoIter: Clone,
{
let iter = it.into_iter();
for s in iter.clone() {
println!("{s}");
}
for s in iter {
println!("{s}");
}
}
and
fn use_iterable_twice<'a, I>(it: I)
where
I: IntoIterator<Item = &'a str> + Clone,
{
for s in it.clone() {
println!("{s}");
}
for s in it {
println!("{s}");
}
}
But I'm not sure which one is better, 1. for the function author, 2. for the caller. And whether there are any performance implications.
If you want to "iterate twice", that's semantically better expressed by the I::IntoIter: Clone bound.
However, the I: Clone bound makes both the where clause and the function implementation slightly simpler, so in a lazy moment, I would probably have chosen the latter over the I::IntoIter: Clone bound.
Furthermore, inexperienced library authors may tend to forget to impl Clone for MyIter while still remembering to implement Clone (and other foundational traits) for their container types, so that's another usability point (although purely speculative/probabilistic) for the latter approach.
Even something as standard like BTreeMap::IntoIter doesn't implement Clone! (In general, it's probably harder to implement Clone for a complicated iterator than a collection.)
Probably not, and it's also highly specific to the iterator so you'd have to measure.
fn use_iterable_twice<I>(it: I)
where for<'a> &'a I: IntoIterator<Item = &'a str>,
{
for s in &it {
println!("{s}");
}
for s in it.into_iter() {
println!("{s}");
}
}
This is probably a better (and definitely a cheaper) solution than cloning is, assuming ownership is isn't needed for both iteration loops.
The only catch is that the value being used as an argument must support that trait bound, which is true for e.g. Vec<T>, but perhaps not for types in general that implement IntoIterator.
It definitely is harder, because Clone on an iterator needs to work for one that is partially-consumed as well. That can get tricky if the iterator is using something like MaybeUninit internally to give away ownership of items before freeing their allocation.
Yes, I had the same thought - not specifically about the BTree iterators but in general about delegating your IntoIterator to another library's collection. You will not have control over the Clone bound. So I thought the I: Clone bound would be more appropriate, but then I also see a library like itertools putting the bound on the I::IntoIterin cartesian_product.
I don't think this works? Rust Playground All four variations in that playground give type mismatches of one or other kind. How do you see it used at call site?
I'm not sure if it's possible to make it work for values and references at the same time,
but for values you'll need to constrain both I and &I:
fn use_iterable_twice<'a, I>(it: I)
where
I: IntoIterator<Item = &'a str>,
for<'x> &'x I: IntoIterator<Item = &'x &'a str>,
{
for s in &it {
println!("{s}");
}
for s in it.into_iter() {
println!("{s}");
}
}