anton lobanov
anton lobanov

Reputation: 41

Problem with lifetimes in rust async dyn trait

Class EvaluationStep should holds dyn Eval<Context> to be evaluated later.
To have simple test for each class, flexibility, easy moving and mixing steps such classes should:

In general it should looks like:

let result = StepN::new(
    // any number of steps in any order...
    StepSecond::new(
        StepFirst::new(initial)
    )
).eval().await

Implementation example

use std::{future::Future, pin::Pin};
///
/// Async trait defined
pub trait Eval<'a, Out> {
    fn eval(&'a mut self) -> Pin<Box<dyn Future<Output = Out> + 'a>>;
}
///
/// The Context to be returned from `Eval`
#[derive(Debug, Clone)]
pub struct Context {
    result: f64,
}
///
/// Some struct implements async trai `Eval`
pub struct EvaluationStep<'a> {
    ctx: Box<dyn Eval<'a, Context> + Send + 'a>,
}
impl<'a> EvaluationStep<'a> {
    ///
    /// New instance [UserHook]
    /// - `ctx` - [Eval]
    pub fn new(ctx: impl Eval<'a, Context> + Send + 'a) -> Self {
        Self { ctx: Box::new(ctx) }
    }
}
impl<'a> Eval<'a, Context> for EvaluationStep<'a> {
    fn eval(&'a mut self) -> Pin<Box<dyn Future<Output = Context> +'a>> {
        Box::pin(async {
            let mut ctx = self.ctx.eval().await;
            ctx.result = 11.1;
            ctx
        })
    }
}
///
/// Fake implements async trai `Eval`
pub struct FakeStep {
    pub ctx: Context,
}
impl<'a> Eval<'a, Context> for FakeStep {
    fn eval(&'a mut self) -> Pin<Box<dyn Future<Output = Context> +'a>> {
        Box::pin(async {
            self.ctx.clone()
        })
    }
}

//
//
#[tokio::main]
async fn main() {
    std::env::set_var("RUST_LOG", "info");
    env_logger::init();
    let mut step = EvaluationStep::new(
        FakeStep { ctx: Context { result: 0.0 } }
    );
    let result = step.eval().await;
    log::info!("Result: {:?}", result);
}

Problem

error[E0597]: `step` does not live long enough
  --> tokio_channel/src/async_trait/main.rs:57:18
   |
54 |     let mut step = EvaluationStep::new(
   |         -------- binding `step` declared here
...
57 |     let result = step.eval().await;
   |                  ^^^^ borrowed value does not live long enough
58 |     log::info!("Result: {:?}", result);
59 | }
   | -
   | |
   | `step` dropped here while still borrowed
   | borrow might be used here, when `step` is dropped and runs the destructor for type `EvaluationStep<'_>`

With single lifetime playground

With two lifetimes playground

Upvotes: 0

Views: 38

Answers (0)

Related Questions