rust

Explicit type annotation in closures


The following code compiles and executes just fine:

fn b(a: &mut u32) -> &mut u32 {
    let f = || {
        *a = 43;
        a
    };
    f()
}

fn main() {
    let mut a: u32 = 42;
    let r = &mut a;
    b(r);
    println!("{:?}", a);
}

If I now explicitly annotate the closure with let mut f = || -> &mut u32, rust complains:

error: captured variable cannot escape `FnMut` closure body
 --> src/main.rs:4:9
  |
1 | fn b(a: &mut u32) -> &mut u32 {
  |      - variable defined here
2 |     let mut f = || -> &mut u32 {
  |                       - inferred to be a `FnMut` closure
3 |         *a = 43;
  |          - variable captured here
4 |         a
  |         ^ returns a reference to a captured variable which escapes the closure body
  |
  = note: `FnMut` closures only have access to their captured variables while they are executing...
  = note: ...therefore, they cannot allow references to captured variables to escape

I can't make sense of this error.


Solution

  • Closures have complex, subtle, and minimally documented inference rules regarding lifetimes and Fn traits. From the error message, I can tell that adding the return type adds FnMut to this closure's inferred traits, however I don't know why.

    If you need to specify the return type, you can fix this by passing the closure through a function that requires FnOnce.

    fn b(a: &mut u32) -> &mut u32 {
        let f = make_fn_once(|| -> &mut u32 {
            *a = 43;
            a
        });
        f()
    }
    
    fn make_fn_once<F, T>(f: F) -> F
    where
        F: FnOnce() -> T,
    {
        f
    }
    

    Note that even defining the closure and then passing it to the function will not work; it has to be passed directly to the function.

    fn b(a: &mut u32) -> &mut u32 {
        // error: captured variable cannot escape `FnMut` closure body
        let f = || -> &mut u32 {
            *a = 43;
            a
        };
        let f = make_fn_once(f);
        f()
    }