Why can't I iterate over the same `vec.iter()` multiple times? - rust

I have a simple program as follows:
let x = vec![1,2,3];
let iterator = x.iter();
for z in iterator {}
for z in iterator {}
As we know, x.iter() is a reference to the value in x, but why cannot I iterate the iterator multiple times? It gives the following error (playground):
error[E0382]: use of moved value: `iterator`
--> src/main.rs:5:14
|
3 | let iterator = x.iter();
| -------- move occurs because `iterator` has type `std::slice::Iter<'_, i32>`, which does not implement the `Copy` trait
4 | for z in iterator {}
| -------- `iterator` moved due to this implicit call to `.into_iter()`
5 | for z in iterator {}
| ^^^^^^^^ value used here after move
|
note: this function takes ownership of the receiver `self`, which moves `iterator`
The problem also exists with (&x).into_iter(), I cannot iterate it multiple times.

Iterators are stateful. An iterator is physically changing itself to loop through the data as it goes. At the end of the first for loop, even if you had ownership over it (which you don't since the for loop took ownership away from you), it would be a useless iterator pointing at the end of the list (one-past-the-end, in C++ terminology) and having no access to the actual data. The point of an iterator is that it statefully walks over the data structure, so to use it you have to consume it.
This is true not just in Rust but in most languages that have a concept of iterators. An "iterable" or "enumerable" data structure (like a list or array) is often iterable multiple times, but an "iterator" as a concept is the thing that does the work of iterating, and it only works once. This allows Rust (and other languages) to have lazy iterators implementing things like .map and .filter and also to iterate over infinite data structures.

Related

Why is a borrowed range not an iterator, but the range is?

An example of how a range gets consumed is:
let coll = 1..10;
for i in coll {
println!("i is {}", &i);
}
println!("coll length is {}", coll.len());
This will fail with
error[E0382]: borrow of moved value: `coll`
--> src/main.rs:6:35
|
2 | let coll = 1..10;
| ---- move occurs because `coll` has type `std::ops::Range<i32>`, which does not implement the `Copy` trait
3 | for i in coll {
| ----
| |
| `coll` moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&coll`
...
6 | println!("coll length is {}", coll.len());
| ^^^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `coll`
The usual way to fix this is to borrow the coll, but that doesn't work here:
error[E0277]: `&std::ops::Range<{integer}>` is not an iterator
--> src/main.rs:3:14
|
3 | for i in &coll {
| -^^^^
| |
| `&std::ops::Range<{integer}>` is not an iterator
| help: consider removing the leading `&`-reference
|
= help: the trait `std::iter::Iterator` is not implemented for `&std::ops::Range<{integer}>`
= note: required by `std::iter::IntoIterator::into_iter`
Why is that? Why is a borrowed range not an iterator, but the range is? Is it interpreting it differently?
To understand what is happening here it is helpful to understand how for loops work in Rust.
Basically a for loop is a short hand for using an iterator, so:
for item in some_value {
// ...
}
is basically a short-hand for
let mut iterator = some_value.into_iter();
while let Some(item) = iterator.next() {
// ... body of for loop here
}
So we can see that whatever we loop over with the for loop, Rust calls the into_iter method from the IntoIterator trait on. The IntoIterator trait looks (approximately) like this:
trait IntoIterator {
// ...
type IntoIter;
fn into_iter(self) -> Self::IntoIter;
}
So into_iter takes self by value and returns Self::IntoIter which is the type of the iterator. As Rust moves any arguments which are taken by value, the thing .into_iter() was called on is no longer available after the call (or after the for loop). That's why you can't use coll in your first code snippet.
So far so good, but why can we still use a collection if we loop over a reference of it as in the following?
for i in &collection {
// ...
}
// can still use collection here ...
The reason is that for a lot of collections C, the IntoIterator trait is implemented not just for the collection, but also for a shared reference to the collection &C and this implementation produces shared items. (Sometimes it is also implemented for mutable references &mut C which produces mutable references to items).
Now coming back to the example with the Range we can check how it implements IntoIterator.
Looking at the reference docs for Range, Range strangely does not seem to implement IntoIterator directly... but if we check the Blanket Implementations section on doc.rust-lang.org, we can see that every iterator implements the IntoIterator trait (trivially, by just returning itself):
impl<I> IntoIterator for I
where
I: Iterator
How does this help? Well, checking further up (under trait implementations) we see that Range does implement Iterator:
impl<A> Iterator for Range<A>
where
A: Step,
And thus Range does implement IntoIterator via the indirection of Iterator. However, there is no implementation of either Iterator for &Range<A> (this would be impossible) or of IntoIterator for &Range<A>. Therefore, we can use a for loop by passing Range by value, but not by reference.
Why can &Range not implement Iterator? An iterator needs to keep track of "where it is", which requires some kind of mutation, but we cannot mutate a &Range because we only have a shared reference. So this cannot work. (Note that &mut Range can and does implement Iterator - more on this later).
It would technically be possible to implement IntoIterator for &Range as that could produce a new iterator. But the likelihood that this would clash with the blanket iterator implementation of Range would be very high and things would be even more confusing. Besides, a Range is at most two integers and copying this is very cheap, so there is really no big value in implementing IntoIterator for &Range.
If you still want to use collection, you can clone it
for i in coll.clone() { /* ... */ }
// `coll` still available as the for loop used the clone
This brings up another question: If we can clone the range and it is (as claimed above) cheap to copy it, why doesn't Range implement the Copy trait? Then the .into_iter() call would copy the range coll (instead of moving it) and it could still be used after the loop. According to this PR the Copy trait implementation actually existed but was removed because the following was considered a footgun (hat tip to Michael Anderson for pointing this out):
let mut iter = 1..10;
for i in iter {
if i > 2 { break; }
}
// This doesn't work now, but if `Range` implemented copy,
// it would produce `[1,2,3,4,5,6,7,8,9]` instead of
// `[4,5,6,7,8,9]` as might have been expected
let v: Vec<_> = iter.collect();
Also note that &mut Range does implement iterator, so you can do
let mut iter = 1..10;
for i in &mut iter {
if i > 2 { break; }
}
// `[4,5,6,7,8,9]` as expected
let v: Vec<_> = iter.collect();
Finally, for completeness, it might be instructive to see which methods are actually called when we loop over a Range:
for item in 1..10 { /* ... */ }
is translated to
let mut iter = 1..10.into_iter();
// ˆˆˆˆˆˆˆˆˆ--- which into_iter() is this?
while let Some(item) = iter.next() { /* ... */ }
we can make this explicit using qualified method syntax:
let mut iter = std::iter::Iterator::into_iter(1..10);
// it's `Iterator`s method! ------^^^^^^^^^
while let Some(item) = iter.next() { /* ... */ }
Ranges are iterators that modify themselves to generate elements. Therefore, to loop over a range, it is necessary to modify it (or a copy of it, as shown below).
Vectors, on the other hand, are not iterators themselves. .into_iter() is called to create an iterator when a vector is looped over; the vector itself doesn't need to be consumed.
The solution here is to use clone to create a new iterator that can be looped over:
for i in coll.clone() {
println!("i is {}", i);
}
(Incidentally, the println! family of macros take references automatically.)
Let's say you have a vector:
let v = vec![1, 2, 3];
The method iter on Vec returns something that implements the Iterator trait. With a vector, there is also an implementation of the trait Borrow (and BorrowMut), that does not return a &Vec though. Instead, you get a slice &[T]. This slice can then be used to iterate over the elements of the vector.
However, the range (e.g. 1..10) implements IntoIterator already and does not need to be transformed into a slice or some other view into it. Therefore, you can consume the range itself by calling into_iter() (which you do implicitly). Now, it is as if you moved the range into some function and you cannot use your variable coll anymore. The borrowing syntax won't help, since this is only some special functionality of Vec.
In this case, you could construct a Vec from your range (with the collect method), clone the range when iterating over it or get the length before iterating (since getting the length doesn't consume the range itself).
Some references:
https://doc.rust-lang.org/std/vec/struct.Vec.html
https://doc.rust-lang.org/std/primitive.slice.html
https://doc.rust-lang.org/std/ops/struct.Range.html

Immutable access in rust

I am new to rust from python and have used the functional style in python extensively.
What I am trying to do is to take in a string (slice) (or any iterable) and iterate with a reference to the current index and the next index. Here is my attempt:
fn main() {
// intentionally immutable, this should not change
let x = "this is a
multiline string
with more
then 3 lines.";
// initialize multiple (mutable) iterators over the slice
let mut lineiter = x.chars();
let mut afteriter = x.chars();
// to have some reason to do this
afteriter.skip(1);
// zip them together, comparing the current line with the next line
let mut zipped = lineiter.zip(afteriter);
for (char1, char2) in zipped {
println!("{:?} {:?}", char1, char2);
}
}
I think it should be possible to get different iterators that have different positions in the slice but are referring to the same parts of memory without having to copy the string, but the error I get is as follows:
error[E0382]: use of moved value: `afteriter`
--> /home/alex/Documents/projects/simple-game-solver/src/src.rs:15:35
|
10 | let afteriter = x.chars();
| --------- move occurs because `afteriter` has type `std::str::Chars<'_>`, which does not implement the `Copy` trait
11 | // to have some reason to do this
12 | afteriter.skip(1);
| --------- value moved here
...
15 | let mut zipped = lineiter.zip(afteriter);
| ^^^^^^^^^ value used here after move
I also get a warning telling me that zipped does not need to be mutable.
Is it possible to instantiate multiple iterators over a single variable and if so how can it be done?
Is it possible to instantiate multiple iterators over a single variable and if so how can it be done?
If you check the signature and documentation for Iterator::skip:
fn skip(self, n: usize) -> Skip<Self>
Creates an iterator that skips the first n elements.
After they have been consumed, the rest of the elements are yielded. Rather than overriding this method directly, instead override the nth method.
You can see that it takes self by value (consumes the input iterator) and returns a new iterator. This is not a method which consumes the first n elements of the iterator in-place, it's one which converts the existing iterator into one which skips the first n elements.
So instead of:
let mut afteriter = x.chars();
afteriter.skip(1);
you just write:
let mut afteriter = x.chars().skip(1);
I also get a warning telling me that zipped does not need to be mutable.
That's because Rust for loop uses the IntoIterator trait, which moves the iterable into the loop. It's not creating a mutable reference, it's just consuming whatever the RHS is.
Therefore it doesn't care what the mutability of the variable. You do need mut if you iterate explicitly, or if you call some other "terminal" method (e.g. nth or try_fold or all), or if you want to iterate on the mutable reference (that's mostly useful for collections though), but not to hand off iterators to some other combinator method, or to a for loop.
A for loop takes self, if you will. Just as for_each does in fact.
Thanks to #Stargateur for giving me the solution. The .skip(1) takes ownership of afteriter and returns ownership to a version without the first element. What was happening before was ownership was lost on the .skip and so the variable could not be mutated anymore (I am pretty sure)

How to take the first element of a BTreeSet without clone [duplicate]

This question already has answers here:
Getting first member of a BTreeSet
(2 answers)
Closed 2 years ago.
I need to implement a function similar to the unstable BTreeSet::pop_first().
It is not a duplicate of Getting first member of a BTreeSet, since I ask for a way get the first element of a BTreeSet without making a copy.
fn pop<O:Ord>(set: &mut std::collections::BTreeSet<O>) -> O
Based on a previous question (Getting first member of a BTreeSet), I get a compiler error about a mutable use after an inmutable one:
pub fn pop<O:Ord>(set: &mut std::collections::BTreeSet<O>) -> O {
let n = {
let mut start_iter = set.iter();
start_iter.next().unwrap()
};
set.take(n).unwrap()
}
error[E0502]: cannot borrow `*set` as mutable because it is also borrowed as immutable
--> src/main.rs:493:5
|
489 | let mut start_iter = set.iter();
| --- immutable borrow occurs here
...
493 | set.take(n).unwrap()
| ^^^^----^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
If I replace set.iter() with set.iter().clone() the code works, but I would like to not make copies of the elements of the set because it will be a costly operation.
How can I implement such a pop function?
rustc version 1.41.0
It is not possible to implement that function like you ask without access to the internals of BTreeSet: The elements of the iterator are borrowed from the set, so n is borrowed from the set as well. While you have borrowed something from the set, you cannot modify it.
You could:
Use clone()/cloned(), like you mentioned. If you were worried it would clone all elements in the iterator when calling pop_first(), that's not the case. It would clone only the one element since iterators are lazy.
Keep both a BinaryHeap and a HashSet of your elements. Before inserting an element into the BinaryHeap, check whether it is already in the HashSet.
Find/implement an extended BTreeSet from scratch that has this functionality.

How can one array contain multiple mutable references to the same object? [duplicate]

This question already has answers here:
How to represent shared mutable state?
(2 answers)
Situations where Cell or RefCell is the best choice
(3 answers)
Need holistic explanation about Rust's cell and reference counted types
(2 answers)
Closed 4 years ago.
I'm making a tile-based game with structures that span multiple tiles. Every tile under the structure has to have a mutable reference to that structure. For a structure that spans two tiles, the 2D array that represents the map has to simultaneously contain two mutable references to the structure.
I also have a list that mutably owns all my structures (for iteration)
I tried this with &mut and failed:
let mut all_structures: Vec<Box<Structure>> = Vec::new();
let mut grid: [[Vec<&mut Box<Structure>>; 4]; 4] = Default::default(); // accessed [y][x]
let mut house: Box<Structure> = Box::new(House { });
grid[1][1].push(&mut house);
grid[1][2].push(&mut house);
all_structures.push(house);
with error:
error[E0499]: cannot borrow `house` as mutable more than once at a time
--> src/main.rs:21:21
|
20 | grid[1][1].push(&mut house);
| ---------- first mutable borrow occurs here
21 | grid[1][2].push(&mut house);
| ---- ^^^^^^^^^^ second mutable borrow occurs here
| |
| first borrow later used by call
Right now this is just single threaded so nothing needs to be thread-safe
I put the code on the playground
The simplest option would be to use Rc<RefCell<Structure>> instead of Box<Structure>.
Rc<T> supports shared ownership of a value of type T, which lets you have multiple pointers to the same object with automatic lifetime management. Rc<T> is not thread-safe; you can change to using Arc instead if you later find you need thread-safety.
RefCell<T> allows you to enforce the Rust borrowing rules (that is, that there can be either one &mut borrow or any number of & borrows) at runtime rather than at compile time: this means it's OK to have multiple paths by which you could potentially mutate the same object, so long as you don't actually do so.
I've put a complete example up on the playground. Note that this uses .borrow() and .borrow_mut(), which will panic if you actually end up violating the borrowing rules (e.g., by mutably borrowing the same Structure multiple times at once).

Why does the compiler tell me to consider using a `let` binding" when I already am?

What is my error and how to fix it?
fn get_m() -> Vec<i8> {
vec![1, 2, 3]
}
fn main() {
let mut vals = get_m().iter().peekable();
println!("Saw a {:?}", vals.peek());
}
(playground)
The compiler's error suggests "consider using a let binding" — but I already am:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:6:45
|
6 | let mut vals = get_m().iter().peekable();
| ------- ^ temporary value dropped here while still borrowed
| |
| temporary value created here
7 | println!("Saw a {:?}", vals.peek());
8 | }
| - temporary value needs to live until here
|
= note: consider using a `let` binding to increase its lifetime
This is obviously a newbie question -- though I thought I'd written enough Rust at this point that I had a handle on the borrow checker... apparently I haven't.
This question is similar to Using a `let` binding to increase value lifetime, but doesn't involve breaking down an expression into multiple statements, so I don't think the problem is identical.
The problem is that the Peekable iterator lives to the end of the function, but it holds a reference to the vector returned by get_m, which only lasts as long as the statement containing that call.
There are actually a lot of things going on here, so let's take it step by step:
get_m allocates and returns a vector, of type Vec<i8>.
We make the call .iter(). Surprisingly, Vec<i8> has no iter method, nor does it implement any trait that has one. So there are three sub-steps here:
Any method call checks whether its self value implements the Deref trait, and applies it if necessary. Vec<i8> does implement Deref, so we implicitly call its deref method. However, deref takes its self argument by reference, which means that get_m() is now an rvalue appearing in an lvalue context. In this situation, Rust creates a temporary to hold the value, and passes a reference to that. (Keep an eye on this temporary!)
We call deref, yielding a slice of type &[i8] borrowing the vector's elements.
This slice implements the SliceExt trait, which does have an iter method. Finally! This iter also takes its self argument by reference, and returns a std::slice::Iter holding a reference to the slice.
We make the call .peekable(). As before, std::slice::Iter has no peekable method, but it does implement Iterator; IteratorExt is implemented for every Iterator; and IteratorExt does have a peekable method. This takes its self by value, so the Iter is consumed, and we get a std::iter::Peekable back in return, again holding a reference to the slice.
This Peekable is then bound to the variable vals, which lives to the end of the function.
The temporary holding the original Vec<i8>, to whose elements the Peekable refers, now dies. Oops. This is the borrowed value not living long enough.
But the temporary dies there only because that's the rule for temporaries. If we give it a name, then it lasts as long as its name is in scope:
let vec = get_m();
let mut peekable = vec.iter().peekable();
println!("Saw a {:?}", vals.peek());
I think that's the story. What still confuses me, though, is why that temporary doesn't live longer, even without a name. The Rust reference says, "A temporary's lifetime equals the largest lifetime of any reference that points to it." But that's clearly not the case here.
This is happening because you are trying to run your .iter().peekable() on the actual vector inside of get_m(), which is getting re-referenced by vals.
Basically, you want something like this:
fn get_m() -> Vec<i8> {
vec![1, 2, 3]
}
fn main() {
let vals = get_m();
let mut val = vals.iter().peekable();
println!("Saw a {:?}", val.peek());
}
(Playground)
Result:
Saw a Some(1)

Resources