Why do closures capture Copy values by reference by default?

It seems that closures without move capture all values by reference. Even Copy values. I wrote a test code:

#![feature(conservative_impl_trait)]

fn mul(x: usize) -> impl Fn(usize, usize) -> usize {
    |a, b| x * (a + b)
}

fn main() {
    println!("{}", mul(2)(1, 3));
}

Because x is captured by reference, the closure to be returned will have an associated lifetime that spans over mul, so this does not compile. The only way (as far as I know) to make this work is to explicitly tell the closure to capture by moving (or, copying) values.

#![feature(conservative_impl_trait)]

fn mul(x: usize) -> impl Fn(usize, usize) -> usize {
    move |a, b| x * (a + b)
}

fn main() {
    println!("{}", mul(2)(1, 3));
}

usize is a Copy type, so why doesn't the closure always capture x by value? Is there any reason?

I think you want to be consistent here irrespective of whether the type is Copy or not. It's certainly plausible (and sometimes necessary, for perf reasons) to pass references to Copy types rather than copying them all the time. I don't think the compiler should adjust code "until it compiles", so to speak - the user should specify what semantics they want. If they meant to pass by reference (e.g. to avoid copying a large Copy type), then maybe they need to fix their code to allow for that. If they're ok with move, then they should specify that.

It makes a semantic difference if the closure modifies a captured variable -- should it modify the original or a private copy?

2 Likes

Okay, I got it - I have forgotten that closures can capture mutable refs. Thanks!