Async-graphql DataLoader lifetime problem

Hi,

I try to use DataLoader with async-graphq.
But I got a lifetime problem that I do not understand.

The profile of the function load is :

/// Trait for batch loading.
#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
pub trait Loader<K: Send + Sync + Hash + Eq + Clone + 'static>: Send + Sync + 'static {
    /// type of value.
    type Value: Send + Sync + Clone + 'static;

    /// Type of error.
    type Error: Send + Clone + 'static;

    /// Load the data set specified by the `keys`.
    #[cfg(feature = "boxed-trait")]
    async fn load(&self, keys: &[K]) -> Result<HashMap<K, Self::Value>, Self::Error>;

And I try to use String or SomeStruct(String) as keys...


#[async_trait]
impl Loader<String> for ParameteByIdLoader {
    type Value = Parameter;
    type Error = async_graphql::Error;

    async fn load(
        &self,
        ids: &[String],
    ) -> Result<HashMap<String, Self::Value>, Self::Error> {

       ...

I have the following error :

error[E0195]: lifetime parameters or bounds on method `load` do not match the trait declaration
  ...
   |
49 |       async fn load(
   |  ______________^
50 | |         &self,
51 | |         ids: &[String],
52 | |     ) -> Result<HashMap<String, Self::Value>, Self::Error> {
   | |_____^ lifetimes do not match method in trait

Can you help me on what usage I am wrong, please ?

E0195 diagnostic should take into account GATs, etc · Issue #135350 · rust-lang/rust · GitHub maybe. Is there more context to the error?

I'm not familiar with the macros in use, but you could try something like

    async fn load<'a: 'a, 'b: 'b>(
        &'a self,
        ids: &'b [String],
    ) -> Result<HashMap<String, Self::Value>, Self::Error> {

(and variations on which lifetimes are named). That's a workaround for the GAT and -> impl Trait use cases.

Thank you for your time.

load is not my function. It is included in the DataLoader module of async-graphql and looks like :

/// Trait for batch loading.
#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
pub trait Loader<K: Send + Sync + Hash + Eq + Clone + 'static>: Send + Sync + 'static {
    /// type of value.
    type Value: Send + Sync + Clone + 'static;

    /// Type of error.
    type Error: Send + Clone + 'static;

    /// Load the data set specified by the `keys`.
    #[cfg(feature = "boxed-trait")]
    async fn load(&self, keys: &[K]) -> Result<HashMap<K, Self::Value>, Self::Error>;

    /// Load the data set specified by the `keys`.
    #[cfg(not(feature = "boxed-trait"))]
    fn load(
        &self,
        keys: &[K],
    ) -> impl Future<Output = Result<HashMap<K, Self::Value>, Self::Error>> + Send;
}

I do not see a lifetime to precise...

And I try also with this code :


#[derive(Clone)]
pub struct Context {
   ...// some values
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct ParameterPrimaryKey(String);

pub struct ParameterLoader {
    pub ctx: Arc<Context>,
}

#[async_trait]
impl Loader<ParameterPrimaryKey> for ParameterLoader {
    type Value = Parameter;
    type Error = graphql_async::Error;

    async fn load(
        &self,
        ids: &[ParameterPrimaryKey],
    ) -> Result<HashMap<ParameterPrimaryKey, Self::Value>, Self::Error> {
        if ids.is_empty() {
            return Ok(HashMap::new());
        }
...

But I have this only this error as diagnostic :

error[E0195]: lifetime parameters or bounds on method `load` do not match the trait declaration
  --> ...   |
49 |       async fn load(
   |  ______________^
50 | |         &self,
51 | |         ids: &[ParameterPrimaryKey],
52 | |     ) -> Result<HashMap<ParameterPrimaryKey, Self::Value>, Self::Error> {
   | |_____^ lifetimes do not match method in trait

I discover maybe I need to enable this feature : boxed-trait Enables async-trait for all traits.
So I update my Cargo.toml.

I continue, seeing other problem in this way.
But I am lost sometimes how to know that we need to enable or not some feature... Not so easy or I miss something in documentation...

I see:

    /// Load the data set specified by the `keys`.
    #[cfg(feature = "boxed-trait")]
    async fn load(&self, keys: &[K]) -> Result<HashMap<K, Self::Value>, Self::Error>;

    /// Load the data set specified by the `keys`.
    #[cfg(not(feature = "boxed-trait"))]
    fn load(
        &self,
        keys: &[K],
    ) -> impl Future<Output = Result<HashMap<K, Self::Value>, Self::Error>> + Send;

It's bad design and not your fault. Features are suppose to be additive, but in this case a feature changes the API. Programs with the feature are incompatible to programs without the feature. They seem marginally aware of the issue.

Have you figured out if you want to use the feature or not? And are you still getting an error? After some playing around, I think you want to use #[async_trait] if you're using boxed-trait, and don't if you're not.

Thank you very much for your time.

Finally I added the box trait feature in Cargo.toml and it works with the async fn load.

1 Like