dtolnay / async-trait

Type erasure for async trait methods
Apache License 2.0
1.81k stars 84 forks source link

The parameter type `AsyncTrait` may not live long enough #101

Closed Luminoth closed 4 years ago

Luminoth commented 4 years ago

I'm not sure if this is just me misusing async_trait or an actual, but I have this trait:

#[async_trait]
pub trait Config {
    async fn read<P>(filename: P) -> anyhow::Result<Self>
    where
        P: Into<PathBuf> + Send,
        for<'de> Self: Deserialize<'de>,
    {
        let filename = filename.into();
        println!("Reading config '{}'...", filename.display());

        let config = tokio::task::spawn_blocking(move || -> anyhow::Result<Self> {
            let file = std::fs::File::open(&filename)?;
            Ok(serde_json::from_reader(file)?)
        })
        .await??;

        Ok(config)
    }
}

Basically just working around from_reader() not being async, and it's producing this error:

error[E0310]: the parameter type `AsyncTrait` may not live long enough
   --> src\config.rs:190:22
  |
  | / #[async_trait]
| | pub trait Config: Send + Sync {
| |_ help: consider adding an explicit lifetime bound...: `AsyncTrait: 'static +`

But I'm not sure if there is a way to do that or if I just need to refactor the method so that it doesn't create that requirement?

dtolnay commented 4 years ago

The compiler's suggestion to add a 'static bound works. Your implementation of read doesn't work if Self is not 'static because spawn_blocking only applies if the return type is 'static. https://docs.rs/tokio/0.2.21/tokio/task/fn.spawn_blocking.html

  use async_trait::async_trait;
  use serde::Deserialize;
  use std::path::PathBuf;

  #[async_trait]
+ pub trait Config: Send + 'static {
      async fn read<P>(filename: P) -> anyhow::Result<Self>
      where
          P: Into<PathBuf> + Send,
          for<'de> Self: Deserialize<'de>,
      {
          let filename = filename.into();
          println!("Reading config '{}'...", filename.display());

          let config = tokio::task::spawn_blocking(move || -> anyhow::Result<Self> {
              let file = std::fs::File::open(&filename)?;
              Ok(serde_json::from_reader(file)?)
          })
          .await??;

          Ok(config)
      }
  }
Luminoth commented 4 years ago

Ahhhh, ok I see. The suggestion showing it on the AsyncTrait was throwing me off, but that totally makes sense. Thank you!