Pls help to explain (weird error with closure lifetime inference)

struct My(i32);

fn call_with_ref<F>(some_closure: F) -> i32
where
    F: for<'a> Fn(&'a My) -> &'a i32,
{
    let value = My(0);
    *some_closure(&value)
}

fn main() {
    
    
    let _f = |arg: &My| &arg.0;
    //This doesn't work
    call_with_ref(_f);
    
    //This is ok.
    //call_with_ref(|arg: &My| &arg.0);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0271]: type mismatch resolving `for<'a> <[closure@src/main.rs:14:14: 14:31] as std::ops::FnOnce<(&'a My,)>>::Output == &'a i32`
  --> src/main.rs:16:5
   |
16 |     call_with_ref(_f);
   |     ^^^^^^^^^^^^^ expected bound lifetime parameter 'a, found concrete lifetime
   |
note: required by `call_with_ref`
  --> src/main.rs:3:1
   |
3  | / fn call_with_ref<F>(some_closure: F) -> i32
4  | | where
5  | |     F: for<'a> Fn(&'a My) -> &'a i32,
6  | | {
7  | |     let value = My(0);
8  | |     *some_closure(&value)
9  | | }
   | |_^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0271`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

Why directly passing the closure is ok but not with a variable binding?

Seems it happens to be a known issue:

1 Like

I’ve seen this type of thing before. One other interesting bit here is coercing the closure to fn(&My) -> &i32, and using that for the call to call_with_ref, makes lifetime region inference (I believe that’s the right term) work.

It's more subtle than that even. Merely assigning it to a local makes it too late to even coerce it anymore.

// works
let f: fn(&My) -> &i32 = |arg: &My| &arg.0;

// fails. wtf?
let f = |arg: &My| &arg.0;
let f: fn(&My) -> &i32 = f;

Yup. As you rightfully note in your comments, it's a true "wtf" :slight_smile:. There are other wtf's in Rust, and it'd be great to resolve them before everyone focuses on newer shinier things (that's essentially the thrust behind this post).

1 Like