Determinant
Determinant

Reputation: 4036

Confused by Rust closure lifetime

I ran into a confusing situation where what the compiler outputs doesn't logically make sense. Here is the minimal example to reproduce the same issue I'm having with my project code.

use std::sync::Arc;

struct A<'a, T> {
    f: Box<dyn Fn(&u32) -> T + 'a>
}

struct B<'a> {
    inner: A<'a, Z<'a>>
}

impl<'a, T> A<'a, T> {
    fn new<F>(f: F) -> Self where F: Fn(&u32) -> T + 'a {
        A { f: Box::new(f) }
    }
}

struct X<'a> {
    _d: &'a std::marker::PhantomData<()>
}

struct Z<'a> {
    _d: &'a std::marker::PhantomData<()>
}

impl<'a> X<'a> {
    fn g(&self, y: u32) -> Z {
        Z { _d: &std::marker::PhantomData }
    }
}

impl<'a> B<'a> {
    fn new(x: Arc<X<'a>>) -> Self {
        B {
            inner: A::new(move |y: &u32| -> Z {
                x.g(*y)
            })
        }
    }
}

fn main() {
}

And the confusing compilation error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> t.rs:35:19
   |
35 |                 x.g(*y)
   |                   ^
   |
note: first, the lifetime cannot outlive the lifetime '_ as defined on the body at 34:27...
  --> t.rs:34:27
   |
34 |             inner: A::new(move |y: &u32| -> Z {
   |                           ^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `x`
  --> t.rs:35:17
   |
35 |                 x.g(*y)
   |                 ^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 31:6...
  --> t.rs:31:6
   |
31 | impl<'a> B<'a> {
   |      ^^
   = note: ...so that the expression is assignable:
           expected B<'a>
              found B<'_>

error: aborting due to previous error

What I don't quite get is what "the lifetime" refers to as mentioned in the log, and what exactly that anonymous lifetime '_ stands for.

Upvotes: 1

Views: 383

Answers (1)

edwardw
edwardw

Reputation: 13942

There was a small oversight in your code. It should be:

impl<'a> X<'a> {
    fn g(&self, y: u32) -> Z<'a> {
        Z { _d: &std::marker::PhantomData }
    }
}

The whole thing then compiles. This is an example of Rust's lifetime elision rule coming into play.

According to the relevant elision rules, namely:

  • Each elided lifetime in input position becomes a distinct lifetime parameter.
  • If there are multiple input lifetime positions, but one of them is &self or &mut self, the lifetime of self is assigned to all elided output lifetimes.

Then to rustc your original code will actually be like following:

impl<'a> X<'a> {
    fn g<'b>(&'b self, y: u32) -> Z<'b> {
        Z { _d: &std::marker::PhantomData }
    }
}

The elided lifetime parameter 'b will come from the calling site, which is exactly what you saw in the error message. The rustc couldn't reconcile the two lifetimes, hence the error.

Upvotes: 2

Related Questions