Repository: rust
Issue: Annotating higher-ranked lifetimes on closures is arduous
Labels: A-lifetimes,A-closures,T-lang

Executive summary: If you want to make a closure returning a higher-ranked lifetime, you need to use a helper like `fn annotate<T,F>(f: F) -> F where F: Fn(&T) -> &T { f }`. We could probably do better.

----

Spawned off of https://github.com/rust-lang/rust/issues/22557#issuecomment-77467069 (and possibly a duplicate of #22340 )

Consider this code ([play](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=550d63c90360f959d74c512aad75bd95)):

```rust
fn main() {
    let f = |x: &i32| x;
    let i = &3;
    let j = f(i);
}
```

It doesn't compile. And its diagnostic is pretty hard to understand.

You can see an explanation from @nikomatsakis about why it doesn't compile here: https://github.com/rust-lang/rust/issues/22557#issuecomment-77467069

With `#![feature(nll)]`, it still doesn't compile; the diagnostic is slightly better:

```
error: lifetime may not live long enough
 --> src/main.rs:4:23
  |
4 |     let f = |x: &i32| x;
  |                 -   - ^ returning this value requires that `'1` must outlive `'2`
  |                 |   |
  |                 |   return type of closure is &'2 i32
  |                 let's call the lifetime of this reference `'1`
```

----

The aforementioned [explanation](https://github.com/rust-lang/rust/issues/22557#issuecomment-77467069) claims that adding a return type will get it to compile. But when I tried that, *it did not work*, both with and without `#![feature(nll)]` ([play](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4c1949c24979c12ed83bdd58dd032a05), which includes NLL since I like its diagnostic here better):

```rust
fn main() {
    let f = |x: &i32| -> &i32 { x };
    let i = &3;
    let j = f(i);
}
```

yields (and we'll leave #58053 in its own bug):

```
error: lifetime may not live long enough
 --> src/main.rs:4:33
  |
4 |     let f = |x: &i32| -> &i32 { x };
  |                 -           -   ^ returning this value requires that `'1` must outlive `'2`
  |                 |           |
  |                 |           return type of closure is &'2 i32
  |                 let's call the lifetime of this reference `'1`
```

So what gives? Well, I think when @nikomatsakis claimed that an explicit return type would work, they were assuming that an explicit return type would cause lifetime elision rules to apply such that the same lifetime would be provided for the input and output reference-types on `f`. But as we saw in #56537, lifetime elision rules do not apply to closure return type annotations.

----

So what we want is to say that we have a lifetime parametric closure, with a type something like `for<'a> Fn(&'a i32) -> &'a i32`. But no, that's not a type, its a trait bound, so this does not work either ([play](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=84058cafa8766434fbb5a92456a6cd3b)):

```rust
fn main() {
    let f: for<'a> Fn(&'a i32) -> &'a i32 = |x| x;
    let i = &3;
    let j = f(i);
}
```

yields:

```
error[E0308]: mismatched types
 --> src/main.rs:4:45
  |
4 |     let f: for<'a> Fn(&'a i32) -> &'a i32 = |x| x;
  |                                             ^^^^^ expected trait std::ops::Fn, found closure
  |
  = note: expected type `dyn for<'a> std::ops::Fn(&'a i32) -> &'a i32`
             found type `[closure@src/main.rs:4:45: 4:50]`

error[E0277]: the size for values of type `dyn for<'a> std::ops::Fn(&'a i32) -> &'a i32` cannot be known at compilation time
 --> src/main.rs:4:9
  |
4 |     let f: for<'a> Fn(&'a i32) -> &'a i32 = |x| x;
  |         ^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `dyn for<'a> std::ops::Fn(&'a i32) -> &'a i32`
  = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
  = note: all local variables must have a statically known size
  = help: unsized locals are gated as an unstable feature
```

----

An approach that *does* work is to feed in the trait bound explicitly via a helper function, like this ([play](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=bd1f8c14fab2930979e892b6e5b6e1c8)):

```rust
fn annotate<T,F>(f: F) -> F where F: Fn(&T) -> &T { f }

fn main() {
    let f = annotate(|x| x);
    let i = &3;
    let j = f(i);
    assert_eq!(*j, 3);
}
```

But that seems like a pretty arduous way to encode a relatively simple pattern. If you look at https://github.com/rust-lang/rust/issues/22340#issuecomment-219794032, you can see others have suggested syntaxes like `for <'a> |x: &'a i32| -> &'a i32 { x }`, (where the `for <'a> CLOSURE_EXPR` is the new interesting expression form), which would be more convenient for addressing cases like ths.