How to return a function pointer with concrete lifetime?

I am given a generic function fn given<T> that I would like to use as the returned value of some other function I write, but I am unable to make it work when T is a reference type.

Below is a simple example demonstrating my problem. I can return the given function as a function pointer when T is a value or using a closure, but not when T is a reference.

fn given<T>(_: T) {}

fn value_is_ok() -> fn(u64) {
    given::<u64>
}

fn ref_not_ok() -> fn(&u64) {
    given::<&u64>
    //|n| given::<&u64>(n) // works for closure
}

fn main() {
    value_is_ok();
    ref_not_ok();
}

The compiler produces the following error:

|
| fn ref_not_ok() -> fn(&u64) {
| -------- expected for<'r> fn(&'r u64) because of return type
| given::<&u64>
| ^^^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter
|
= note: expected type for<'r> fn(&'r u64)
found type fn(&u64) {given::<&u64>}

I can see the issue is that given::<&u64> doesn't have a lifetime to match the return type fn(&u64). But what return type should I use instead? Thank you!

I'm not sure under what circumstances code like this would be useful, but ignoring any potential XY problems, the direct answer to your immediate question is...

The error message is almost right. It doesn't make any sense to return a reference with a lifetime pulled out of thin air. But it does make sense to return a function that takes "any" lifetime, which is what the for<'r> syntax means.

The reason this doesn't compile is that the & in given::<&u64> needs to have the same lifetime (or a longer shorter lifetime) as the & in ref_not_ok's return type. Otherwise, the types don't match up, and letting this compile would in general be just as bad as letting you return a non-'static &str from a function with return type &'static str. Both &s can be any lifetime, but they need to be "the same any" lifetime, which is something you express in Rust by declaring a lifetime parameter for the scope of the whole function instead of just its return type:

fn ref_not_ok<'r>() -> fn(&'r u64) {
    given::<&'r u64>
}

Again, I have no idea why this would be useful, but that is a thing that compiles.

1 Like

The code I posted is a simplified example. My original problem was to figure out what concrete F to use in returning a std::iter::Map<I, F>. The final segment of the Map happens to be a generic function applied to a reference type. I just got it to compile by adding a lifetime parameter following your solution.

Thank you very much for your reply!

1 Like

Just nitpicking, but it should actually be the same or shorter. If we claim to return a fn(&'veryshort u64) but actually return a fn(&'static u64), then calling the returned function with a &'veryshort u64 would be invalid, as the function assumes the argument lives for longer than it does.

This inversion of subtyping is called contravariance.

1 Like