I am just getting started with Rust and am quickly falling in love with it.
I love the concept of ownership and borrowing but am unsure of how this applies to for loops in all contexts.
I have found a bunch of information on for loops in Rust and how they map to this construct under the hood
let mut it = values.into_iter();
loop {
match it.next() {
Some(x) => println!("{}", x),
None => break,
}
}
They move their values by default (into_iter), and I understand you can use a borrow (reference) instead like so:
for v in &mut vec {
....
}
or
for v in vec.iter_mut() {
....
}
but what I'm curious about is as with a function where you can take ownership, and then return it to the caller, is there a way of doing this in a for loop in Rust, or must you always use a borrow. Essentially is there any way of getting ownership back from a for loop?
I don't think there's strictly a way of getting back ownership. If you could, the Vec would be empty when it was returned from the for loop anyway (unless you break before the end maybe?), so it would be pretty much like using drain. Is there a specific use case you're looking for, or is drain good enough?
Not specific to loops, but if you give up ownership, you're done with the object you give up. For example, let's say that I have an owned identity function:
fn<T> id(t: T) -> T { t }
When you call this function, you move your T (whatever T is) into the function (unless your type is Copy, in which case you could be making a copy). At the end of the function you again move the T back to the caller.
Now if you wanted to do that with a loop, you'd need a way to return the original object from the loop, however, you've already moved the object into its .into_iter() method. Even the iterator object that method returns is given to the loop. So unless your value is Copy, it's gone.
Ah okay I see, so then I guess I don't understand the context in which you would use a for loop without using the container or array as a reference. When would you want the behaviour you describe?
I recommend checking out my Rustcamp talk on this problem: OMG ITERATORS
Basically there's 4 kinds of iteration:
iter, iter_mut, into_iter, and drain. Each has different tradeoffs for what you want to do with both the data and the container. Usually, iterating by-reference is the goto, because destructive iteration is really heavy-handed for most problems (but is useful when you need it!).
Sorry to bump this old thread but it is about the first thing that popped up when I was searching for an answer to a similar problem.
I had written my first iterator, which pulls values from a PRNG, and could use it like so:
let mut pcg32 = Pcg32::new();
for r in pcg32.take(8) {
println! ("random u32 = {0:010}", r);
}
But then of course I could not use it again:
for r in pcg32.take(8) {
println! ("random u32 = {0:010}", r);
}
Errors out:
17 | for r in pcg32.take(8) {
| ^^^^^ value used here after move
A solution was to use .by_ref() :
let mut pcg32 = Pcg32::new();
for r in pcg32.by_ref().take(8) {
println! ("random u32 = {0:010}", r);
}
for r in pcg32.by_ref().take(8) {
println! ("random u32 = {0:010}", r);
}
Now of course I'm curious as to whether that is the best way to do this?
I'm afraid I'm no Rust expert, but in this scenario I believe using by_ref() is just what you need (there's some useful info on it here - Iterator in std::iter - Rust)
I remember this article helping be better understand some of Rust's quirks with for loops too - for loops in Rust
Thanks. I suspect you are right. Odd it was not mentioned here all those years ago.
I have things working now and producing correct results although not quite the way I would have expected to do it. But that might be down to the way I have implemented my PRNG and the iterator for it. Perhaps a question for another thread.
It is a little strange when coming from other languages which don't really have this concept.
My original question was really 'is there a valid syntax to move a value(s) in and out of a loop' (like you technically can with a function), which it seems like there's not.
The follow-up was about when in idiomatic Rust would you ever want to use this mechanism (move a value while iterating). One contrived example might be moving a value from one container to another (say iterating over a vector moving the values to a set).
I do think it comes up less often in practice but as @Gankra mentions it does have its uses.
Literally the same, since iter.by_ref() is just an alternate way of writing &mut iter:
So instead of pcg32.by_ref().take(8), you could also write:
for r in (&mut pcg32).take(8) { ... }
This works because there is a blanket impl<I: Iterator> Iterator for &mut I in the standard library. So a mutable reference to an iterator can be used anywhere an iterator is expected.
Presumably it is because pcg32, in this case, is an iterator. As such it has internal state that gets mutated as the iteration proceeds. So a plain & is not sufficient.