Need help figuring out lifetime annotations


#1

So, while playing around with crypto code, it seems that I have accidentally created a higher-order function which does not accept closure parameters. I would like to know how to fix this.

A minimal code example which reproduces the compiler error is this:

fn higher_order<F>(functor: &F) -> String
    where F: Fn(&String) -> String
{
    unimplemented!()
}

fn main() {
    let closure = |s| unimplemented!();
    println!("{}", higher_order(&closure));
}

For this example, the compiler errors are:

error[E0281]: type mismatch: the type `[closure@src/main.rs:8:19: 8:39]` implements the trait `std::ops::Fn<(_,)>`, but the trait `for<'r> std::ops::Fn<(&'r std::string::String,)>` is required (expected concrete lifetime, found bound lifetime parameter )
 --> src/main.rs:9:20
  |
9 |     println!("{}", higher_order(&closure));
  |                    ^^^^^^^^^^^^
  |
  = note: required by `higher_order`

error[E0271]: type mismatch resolving `for<'r> <[closure@src/main.rs:8:19: 8:39] as std::ops::FnOnce<(&'r std::string::String,)>>::Output == std::string::String`
 --> src/main.rs:9:20
  |
9 |     println!("{}", higher_order(&closure));
  |                    ^^^^^^^^^^^^ expected bound lifetime parameter , found concrete lifetime
  |
  = note: concrete lifetime that was found is lifetime '_#24r
  = note: required by `higher_order`

error: aborting due to 2 previous errors

From the error messages and a couple of web searches, it looks like this originates from incorrect automatic lifetime inference and an appropriate lifetime annotation might be the solution, but all my attempts in this direction so far have failed.

EDIT: Even stranger, adding type annotations to the closure parameters will make the code compile:

fn higher_order<F>(functor: &F) -> String
    where F: Fn(&String) -> String
{
    unimplemented!()
}

fn main() {
    let closure = |s: &String| unimplemented!();
    println!("{}", higher_order(&closure));
}

#2

Are you sure the problem is really with lifetimes? The first error is a type mismatch. It looks like it cannot infer the correct type for the argument and return types of the closure. Maybe try a more explicit closure that actually returns a returns a String?


#3

Indeed, closer investigation revealed that adding a type annotation to the closure leads the code to compile. Maybe my minimal example is not as close to the original as I thought…

I’m now trying to add a type annotation to the original code’s closure and see if it compiles. If so, this could be a type inference bug instead.

EDIT: Confirmed. Trying to produce a minimal example of the type inference failure…

EDIT 2: So, as it turns out, this compiles…

fn higher_order<F>(functor: &F) -> String
    where F: Fn(&String) -> String
{
    unimplemented!()
}

fn function(str: &String) -> String {
    unimplemented!()
}

fn main() {
    let closure = |s: &String| function(s);
    println!("{}", higher_order(&closure));
}

…but if I remove the closure type annotation, the compilation fails:

fn higher_order<F>(functor: &F) -> String
    where F: Fn(&String) -> String
{
    unimplemented!()
}

fn function(str: &String) -> String {
    unimplemented!()
}

fn main() {
    let closure = |s| function(s);
    println!("{}", higher_order(&closure));
}

The error message for the later version is closer to what I had in my original code:

error[E0281]: type mismatch: the type `[closure@src/main.rs:12:19: 12:34]` implements the trait `std::ops::Fn<(&std::string::String,)>`, but the trait `for<'r> std::ops::Fn<(&'r std::string::String,)>` is required (expected concrete lifetime, found bound lifetime parameter )
  --> src/main.rs:13:20
   |
13 |     println!("{}", higher_order(&closure));
   |                    ^^^^^^^^^^^^
   |
   = note: required by `higher_order`

error[E0271]: type mismatch resolving `for<'r> <[closure@src/main.rs:12:19: 12:34] as std::ops::FnOnce<(&'r std::string::String,)>>::Output == std::string::String`
  --> src/main.rs:13:20
   |
13 |     println!("{}", higher_order(&closure));
   |                    ^^^^^^^^^^^^ expected bound lifetime parameter , found concrete lifetime
   |
   = note: concrete lifetime that was found is lifetime '_#1r
   = note: required by `higher_order`

error: aborting due to 2 previous errors

Is this an expected behaviour, or should I report it as a rustc bug?


#4

I’m not sure why it needs a lifetime parameter at all, you’re not returning a reference. Unless it has to do with inferring how to create the closure, via a reference or a move of function. I’m not sure how that should even work for a function.


#5

…and, more importantly, why does adding a type annotation (not a lifetime annotation) to the closure make the code compile?


#6

I think this is another example of https://github.com/rust-lang/rust/issues/41078


#7

It does look and feel very similar!