rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
99.14k stars 12.8k forks source link

Adding explicit lifetimes causes compilation error #131336

Open varqox opened 1 month ago

varqox commented 1 month ago

Compiler version 1.81.0.

I am writing an asynchronous function that takes asynchronous callback, I managed to write it so that it compiles:

pub trait Callback<State> {
    type Output: Future<Output = ()>;

    fn call(&self, state: State) -> Self::Output;
}

impl<State, F, Fut> Callback<State> for F
where
    F: Fn(State) -> Fut,
    Fut: Future<Output = ()>,
{
    type Output = Fut;

    fn call(&self, state: State) -> Self::Output {
        self(state)
    }
}

pub async fn aaa<State, Cb>(state: &mut State, callback: &Cb)
where
    for<'state> Cb: Callback<&'state mut State>,
{
}

pub async fn bbb<State, Cb>(state: &mut State) {
    async fn callback<State>(state: &mut State) {}
    aaa(state, &callback).await;
}

However, if I try to add explicit lifetimes to the Callback trait, I get compilation error:

error[E0309]: the parameter type `State` may not live long enough
   --> src/storage.rs:252:33
    |
250 | impl<'state, State, F, Fut> Callback<'state, State> for F
    |      ------ the parameter type `State` must be valid for the lifetime `'state` as defined here...
251 | where
252 |     F: Fn(&'state mut State) -> Fut,
    |                                 ^^^ ...so that the reference type `&'state mut State` does not outlive the data it points at
    |
help: consider adding an explicit lifetime bound
    |
250 | impl<'state, State: 'state, F, Fut> Callback<'state, State> for F
    |                   ++++++++

for the following code:

pub trait Callback<'state, State> {
    type Output: Future<Output = ()>;

    fn call(&self, state: &'state mut State) -> Self::Output;
}

impl<'state, State, F, Fut> Callback<'state, State> for F
where
    F: Fn(&'state mut State) -> Fut,
    Fut: Future<Output = ()>,
{
    type Output = Fut;

    fn call(&self, state: &'state mut State) -> Self::Output {
        self(state)
    }
}

pub async fn aaa<State, Cb>(state: &mut State, callback: &Cb)
where
    for<'state> Cb: Callback<'state, State>,
{
}

pub async fn bbb<State, Cb>(state: &mut State) {
    async fn callback<State>(state: &mut State) {}
    aaa(state, &callback).await;
}

But if I fix it according to the suggestion, I get the following error:

error[E0310]: the parameter type `State` may not live long enough
   --> src/storage.rs:270:5
    |
270 |     aaa(state, &callback).await;
    |     ^^^^^^^^^^^^^^^^^^^^^
    |     |
    |     the parameter type `State` must be valid for the static lifetime...
    |     ...so that the type `State` will meet its required lifetime bounds
    |
help: consider adding an explicit lifetime bound
    |
268 | pub async fn bbb<State: 'static, Cb>(state: &mut State) {
    |                       +++++++++

error[E0310]: the parameter type `State` may not live long enough
   --> src/storage.rs:270:27
    |
270 |     aaa(state, &callback).await;
    |                           ^^^^^
    |                           |
    |                           the parameter type `State` must be valid for the static lifetime...
    |                           ...so that the type `State` will meet its required lifetime bounds
    |
help: consider adding an explicit lifetime bound
    |
268 | pub async fn bbb<State: 'static, Cb>(state: &mut State) {
    |                       +++++++++

for the code

pub trait Callback<'state, State> {
    type Output: Future<Output = ()>;

    fn call(&self, state: &'state mut State) -> Self::Output;
}

impl<'state, State: 'state, F, Fut> Callback<'state, State> for F
where
    F: Fn(&'state mut State) -> Fut,
    Fut: Future<Output = ()>,
{
    type Output = Fut;

    fn call(&self, state: &'state mut State) -> Self::Output {
        self(state)
    }
}

pub async fn aaa<State, Cb>(state: &mut State, callback: &Cb)
where
    for<'state> Cb: Callback<'state, State>,
{
}

pub async fn bbb<State, Cb>(state: &mut State) {
    async fn callback<State>(state: &mut State) {}
    aaa(state, &callback).await;
}

I thing something is not correct about treating explicit lifetimes by the compiler, or I added the lifetimes incorrectly. Please help.

lolbinarycat commented 1 month ago

here's a working version:

use std::future::Future;
pub trait Callback<'state, State> {
    type Output: Future<Output = ()>;

    fn call(&self, state: &'state mut State) -> Self::Output;
}

impl<'state, State: 'state, F, Fut> Callback<'state, State> for F
where
    F: Fn(&'state mut State) -> Fut,
    Fut: Future<Output = ()> + 'state,
{
    type Output = Fut;

    fn call(&self, state: &'state mut State) -> Self::Output {
        self(state)
    }
}

pub async fn aaa<State, Cb>(state: &mut State, callback: &Cb)
where
    for<'state> Cb: Callback<'state, State>,
{
}

pub async fn bbb<State: 'static, Cb>(state: &mut State) {
    async fn callback<State>(state: &mut State) {}
    aaa(state, &callback).await;
}
varqox commented 1 month ago

Okay, I wanted to make the example simple, but lost the issue real issue along the way. Thanks for the solution to the original problem. This code works:

use std::future::Future;
pub trait Callback<State> {
    type Output: Future<Output = ()>;

    fn call(&self, state: State) -> Self::Output;
}

impl<State, F, Fut> Callback<State> for F
where
    F: Fn(State) -> Fut,
    Fut: Future<Output = ()>,
{
    type Output = Fut;

    fn call(&self, state: State) -> Self::Output {
        self(state)
    }
}

pub async fn aaa<State, Cb>(state: &mut State, callback: &Cb)
where
    for<'state> Cb: Callback<&'state mut State>,
{
}

pub async fn bbb<State, Cb>(state: &mut State) {
    struct InnerState<'state, State> {
        state: &'state mut State,
        x: i32,
    }

    async fn callback<'a, State>(state: &mut InnerState<'a, State>) {}

    aaa(&mut InnerState { state, x: 42 }, &callback).await;
}

However, this does not compile:

use std::future::Future;
pub trait Callback<'state, State> {
    type Output: Future<Output = ()>;

    fn call(&self, state: &'state mut State) -> Self::Output;
}

impl<'state, State: 'state, F, Fut> Callback<'state, State> for F
where
    F: Fn(&'state mut State) -> Fut,
    Fut: Future<Output = ()>,
{
    type Output = Fut;

    fn call(&self, state: &'state mut State) -> Self::Output {
        self(state)
    }
}

pub async fn aaa<State, Cb>(state: &mut State, callback: &Cb)
where
    for<'state> Cb: Callback<'state, State>,
{
}

pub async fn bbb<State: 'static, Cb>(state: &mut State) {
    struct InnerState<'state, State> {
        state: &'state mut State,
        x: i32,
    }

    async fn callback<'a, State>(state: &mut InnerState<'a, State>) {}

    aaa(&mut InnerState { state, x: 42 }, &callback).await;
}

with error:

error[E0521]: borrowed data escapes outside of function
  --> src/lib.rs:34:5
   |
26 | pub async fn bbb<State: 'static, Cb>(state: &mut State) {
   |                                      -----  - let's call the lifetime of this reference `'1`
   |                                      |
   |                                      `state` is a reference that is only valid in the function body
...
34 |     aaa(&mut InnerState { state, x: 42 }, &callback).await;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |     |
   |     `state` escapes the function body here
   |     argument requires that `'1` must outlive `'static`
lolbinarycat commented 1 month ago

this type of question would probably be better directed to URLO