Force capture of a variable by value while capturing others by reference


#1

I’m a few days into learning rust and have come across a problem of this sort:

struct NoCopy<T>(T);
fn main() {
    let c = NoCopy(42);
    let i = vec![1, 2, 4].into_iter();
    let res = i.flat_map(|a| {
        (0..5).map(|b| a + b + c.0)
    });
    for x in res {
        println!("{}", x);
    }
}

This won’t compile because the inner closure borrows a from the outer closure, but will outlive it. I therefore need to instruct the compiler that the inner closure should instead capture a by value. However, the move keyword will try to capture everything by value, including c. Since the type of c does not implement Copy, this fails since the outer closure is FnMut.

I also tried adding let a = a; into the inner closure, which normally forces a move (I think?), but I get the same \a` does not live long enough` error here. (Meta: Any way to escape these backticks?)

Is there any way to do this? I take it there are no C++11-style capture lists in Rust – why not? Is there a fundamental safety issue, or is it just not yet implemented?


#2

You can do this instead:

    let res = i.flat_map(|a| {
        let cr = &c.0;
        (0..5).map(move |b| a + b + cr)
    });

So capture c.0 by reference, and then move in the inner closure.


Basic question about closures, move, Vec<T>
#3

Actually, I can even do:

    let res = i.flat_map(|a| {
        let cr = &c;
        (0..5).map(move |b| a + b + cr.0)
    });

which is more generally useful (e. g. for me, c is a HashMap and I perform lookups in the inner lambda.) Still, quite a hoop to jump through.


#4

Yeah, indeed it doesn’t matter since you’re creating an immutable reference and those are fine to move (actually, copy) around.

If you squint, that’s your capture list specification :slight_smile:.


#5

I guess that is a way to look at it :smile: especially since I don’t even need to change the name, let c = &c; works just as well. It’s a bit verbose, but, oh well. Thanks!


#6

Yup, you can shadow the binding.

By the way, the need to sometimes borrow some of the environment and move the rest was discussed a bit yesterday here: Basic question about closures, move, Vec<T>


#7

I always seem to run into those immediately …