Good way to move out of captured variable in closure

Is there a good way of moving a captured variable out of a closure without cloning it? I have a situation similar to this:

let mut partial_result = String::new();
let mut final_results = vec![];

// Some iterator on which 'for_each' is called
(0..15).for_each(|_| {
    partial_result.push('a'); // builds the partial result further
    if partial_result.len() > 5 { // condition for when the partial result is done
        // save and reset partial_result
        final_results.push(partial_result)
        partial_result = String::new();
    }
});
println!("Result: {final_results:?}");

However as is this causes an error. I would like final_results to take over ownership of the current value in partial_result and then reset partial_result back to it's starting value. In this simple example it can be solved by calling .clone() on partial_result before pushing it. However in the actual code I have some structs which don't necessarily implement Clone<T> so I was wondering if there is some clever way of moving the current value and then immediately resting it to a fixed value in Rust or if I have to make partial_result clonable?

use std::mem::take(), since String: Default:

final_result.push(std::mem::take(&mut partial_result));
3 Likes

You will need to create a new allocation, but you can clear the string simultaneously and without moving using drain:

fn main() {
    let mut partial_result = String::new();
    let mut final_results = vec![];

    // Some iterator on which 'for_each' is called
    (0..15).for_each(|_| {
        partial_result.push('a'); // builds the partial result further
        if partial_result.len() > 5 {
            // condition for when the partial result is done
            // save and reset partial_result
            final_results.push(partial_result.drain(..).collect::<String>());
        }
    });

    println!("Result: {final_results:?}");
}

Playground.

This looks like exactly what I was looking for. Thanks!

This specificly only works when partial_result is a string right? In my case partial_result is not necessarily a String but rather some struct.

Other containers like Vec or HashMap also have built-in support for a draining iterator. For your own struct you'd need to implement that yourself.

use std::mem::replace() for types that aren't Default, or you want to replace it with a new value different than Default::default().

mem::take(&mut variable) is same as mem::replace(&mut variable, Default::default()).

2 Likes

Thanks for the tip! In my case most types I use are Default but still good to keep in mind.

Option::take can also be useful if you need at least one element to meaningfully initialize the accumulator, or if you know that you’ll only ever be producing a single value.