Dan
Dan

Reputation: 13160

Why does this closure's lifetime change based on seemingly unrelated types?

With the code

fn foo<'a, 'b>(
    state: &'b mut i32,
) -> impl FnMut(&'a str) -> &'static str + 'b {
    |s| "hi"
}

I get an error

error[E0482]: lifetime of return value does not outlive the function call
112 | ) -> impl FnMut(&'a str) -> &'static str + 'b {
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: the return value is only valid for the lifetime `'a` as defined on the function body at 110:9
110 | fn foo1<'a, 'b>(
    |         ^^

But somehow the code

fn foo2<'a, 'b>(
    state: &'b mut Option<&'a i32>,
) -> impl FnMut(&'a str) -> &'static str + 'b {
    |s| "hi"
}

compiles without errors. Why does the type of state change the lifetime of the closure?

Upvotes: 4

Views: 70

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65752

With type &'b mut Option<&'a i32>, Rust infers the lifetime bound 'a: 'b ('a outlives 'b). This bound is required for the function signature to be well-formed. You can add this bound explicitly to avoid the error:

fn foo<'a, 'b>(
    state: &'b mut i32,
) -> impl FnMut(&'a str) -> &'static str + 'b
where
    'a: 'b,
{
    |s| "hi"
}

However, if no parameter uses 'a, then 'a should be a higher-rank trait bound instead:

fn foo<'b>(
    state: &'b mut i32,
) -> impl for<'a> FnMut(&'a str) -> &'static str + 'b {
    |s| "hi"
}

The Fn family of traits are special in that they allow you to omit the lifetime entirely (following the same elision rules as fn signatures), so this is equivalent:

fn foo<'b>(
    state: &'b mut i32,
) -> impl FnMut(&str) -> &'static str + 'b {
    |s| "hi"
}

Upvotes: 7

Related Questions