Error when passing closure to function expecting for<'_> FnOnce, but only with lvalue

Hi all,

I've come up with a reduced example of the issue I'm facing:

Playground

My confusion stems from the fact that it works if I pass the closure in directly, without binding it to a name first, but it doesn't work if I try to pass it in via a bound name.

I can imagine this is because binding it to a name first passes in concrete lifetime parameters. However, this works - I'm able to pass the closure in via a binding: Playground

It seems like the difference is that I'm directly passing in a reference. From a few old answers on StackOverflow, I have a vague impression that having taking a reference in the closure causes the compiler to generate the for<'_> FnOnce that's needed, but it's a bit unclear. However, in the code I'm dealing with, I can't take a reference, because I need to consume the object that's passed in. Is there any way otherwise I can convince the compiler to generate a closure that's for<'_> FnOnce? Or is this the wrong approach?

Additionally, is there any flag I can pass to the compiler to unpretty the closure that's generated, and/or any documentation that goes into a bit more detail about the the details of the types that the compiler generates for the closure? Something a bit like what's done for C++ on CppInsights -- see here -- where the compiler-generated type is shown? Alternatively, anything I should read to get a better intuition of the generated type?

Thanks in advance.

1 Like

Note that assigning the closure also fails if you don't use the variable:

let bound = the_closure!();
// exec(bound);

So based on that I have created a much smaller example of the same problem:

fn main() {
    let closure = |s: &str| s;
}

playground

This gives the error

error: lifetime may not live long enough
 --> src/main.rs:2:29
  |
2 |     let closure = |s: &str| s;
  |                       -   - ^ returning this value requires that `'1` must outlive `'2`
  |                       |   |
  |                       |   return type of closure is &'2 str
  |                       let's call the lifetime of this reference `'1`

It appears that the compiler is not able to figure out that it has to assign the same lifetime to the argument and return value.

To force the compiler to chain the lifetimes together, you can pass it through a no-op:

fn identity<T: ?Sized, F>(f: F) -> F
where
    F: for<'a> Fn(&'a T) -> &'a T,
{
    f
}

fn foo(f: impl for<'a> Fn(&'a str) -> &'a str) {}

fn main() {
    let closure = identity(|s: &str| s);
    foo(closure);
}

playground

1 Like

Brilliant! Thank heaps!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.