Iterating through a Range<T>
seems to consume the range instance as into_iter
function takes the ownership of the range. By looking at the documentation of range, it is clear that the Borrow
trait is only implemented for dynamic range objects. Without cloning, would it be possible to iterate through a range while also passing immutable references of the range to other functions?
let numbers = 500..4000;
// ERROR [(E0277)]: the trait `std::iter::Iterator` is not implemented for `&std::ops::Range<i32>`
for n in &numbers {
println!("{}", n);
do_something_else(&numbers);
reuse_range(&numbers);
}
// Surprisingly, there are no errors when it comes to argument type of functions.
fn do_something_else(range: &Range<i32>) { }
fn reuse_range(range: &Range<i32>) { }
As seen above, functions can take a borrow of Range<T: Sized>
but the compiler itself does not allow borrowing ranges.
So far I have tried using Box
smart pointer but the behaviour is the same.
Simply, by_ref()
is available, but that would also restrict us from borrowing as immutable since we already would have a mutable borrow of the same object.
First, let's look at the implementations:
Range
implements Iterator
. for
loops desugar to a call to std::iter::IntoIterator::into_iter
, which is implemented for everything which is already an iterator (since you can obviously create an iterator from an iterator -- just return the iterator). Additionally, Iterator
is implemented for (and only for) &mut
references to existing iterators.
From this we can deduce the error:
IntoIterator
.Iterator
into an Iterator
through IntoIterator
. Which does nothing.Iterator
is only implemented for unique (&mut
) references to other Iterator
s.Hence, you can't iterator over &Range
s, and instead only over &mut Range
s, or Range
s.
What should be done instead is to either Clone
the Range
:
let my_range = 10..40;
for i in my_range.clone() {
println!("{:?}", i);
}
Take a mutable reference to the range (thereby emptying it too):
let mut my_range = 10..40;
for i in &mut my_range {
println!("{:?}", i);
}
assert_eq!(my_range.next(), None);
Or do the more idiomatic action of just building the range each time:
for i in 10..40 {
println!("{:?}", i);
}
Which is extremely cheap to do.
Additionally, these rules about implementations of the Iterator
trait for &mut
and not &
references apply to all iterators. This allows for us to do something like this:
let mut my_iter = 0..100;
// Only take first 50 elements.
for x in (&mut my_iter).take(50) {
println!("{:?} < 50", x);
}
for x in my_iter {
println!("{:?} >= 50", x);
}
Note that take
takes self
, however self
is &mut Range
, so we don't use up the original Range
.