Is it possible to take ownership of data back from a for loop once the loop completes

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?

Thank you very much for your time and help!

All the best,

Tom

1 Like

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?

Thank you for the explanations! Very helpful!

As an example, if I write this

for v in vec {
    println!("{}", v);
}

followed immediately by..

for v in vec {
    println!("{}", v);
}

..again, then I get this 'error: use of moved value: vec'

Which I understand, but then does this mean you should only really use a for loop with a borrow, so &vec, not vec.

Thanks again!

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!).

3 Likes

Sweet I'll check out the link, thank you!

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?

Hey @ZiCog,

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

I hope that helps a little!

Best of luck :slight_smile:

Tom

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.

I highly recommend this article too - Effectively Using Iterators In Rust

1 Like

With function parameters one passes in a reference if one wants to use that same data after the function call.

As far as I can tell using .by_ref() does the same for looping over an iterator, as in my example above.

Still a bit murky to me.

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.

2 Likes

BINGO! that is just the syntax I had been searching for. Just have to get the braces in the right place.

Thanks.

@mbrubeck I'm just curious but is there a reason why it needs to be a mut& and not just a &? Good to know that syntax though thanks!

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.

Ah yes of course, got you thanks :+1:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.