dtolnay / async-trait

Type erasure for async trait methods
Apache License 2.0
1.84k stars 85 forks source link

References or lifetimes in generic types results in `lifetimes in impl do not match this method in trait` #175

Closed EliseZeroTwo closed 3 years ago

EliseZeroTwo commented 3 years ago

Hi!

Seemingly providing references or lifetimes in generic types breaks async-trait.

Tested on: rustc 1.56.0-nightly (ad981d58e 2021-08-08) & rustc 1.54.0 (a178d0322 2021-07-26)

Code used to replicate this:

#[async_trait(?Send)]
pub trait DbOpDelete<IdT>: Sized {
    async fn delete(pool: &sqlx::PgPool, id: IdT) -> Result<Option<Self>, bool>;
}

#[async_trait(?Send)]
impl DbOpDelete<&str> for DbCustomer {
    async fn delete(pool: &sqlx::PgPool, id: &str) -> Result<Option<Self>, bool> {
        Err(false)
    }
}

Error:

error[E0195]: lifetime parameters or bounds on method `delete` do not match the trait declaration
  --> src/schema/customer.rs:31:14
   |
31 |     async fn delete(pool: &sqlx::PgPool, id: &str) -> Result<Option<Self>, bool> {
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait
   |
  ::: src/schema.rs:37:14
   |
37 |     async fn delete(pool: &sqlx::PgPool, id: IdT) -> Result<Option<Self>, bool>;
   |              ------------------------------------ lifetimes in impl do not match this method in trait

🖤

taiki-e commented 3 years ago

Workaround: using an explicit lifetime:

  #[async_trait(?Send)]
- impl DbOpDelete<&str> for DbCustomer {
+ impl<'a> DbOpDelete<&'a str> for DbCustomer {
-     async fn delete(pool: &sqlx::PgPool, id: &str) -> Result<Option<Self>, bool> {
+     async fn delete(pool: &sqlx::PgPool, id: &'a str) -> Result<Option<Self>, bool> {
          Err(false)

related: #106 #64

taiki-e commented 3 years ago

proc-macro cannot recognize that the anonymous lifetime in the method argument and the anonymous lifetime in the trait parameter are the same, so IIUC there is no way to automatically resolve this on the proc-macro side.

EliseZeroTwo commented 3 years ago

Thank you for the fast response! With this I run into another problem (both on nightly & stable):

Code:

pub struct OwO;

#[async_trait(?Send)]
pub trait Trait<IdT>: Sized {
    async fn test(id: IdT) -> Self;
}

#[async_trait(?Send)]
impl<'a> Trait<&'a str> for OwO {
    async fn test(id: &'a str) -> Self {
        todo!()
    }
}

Error:

error[E0276]: impl has stricter requirements than trait
  --> src/main.rs:12:5
   |
7  |     async fn test(id: IdT) -> Self;
   |     ------------------------------- definition of `test` from trait
...
12 |     async fn test(id: &'a str) -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `'a: 'async_trait`

For more information about this error, try `rustc --explain E0276`.

However this seems like a separate issue, if this is not just me missing something obvious would you like me to open a new issue?

taiki-e commented 3 years ago

That seems related to #8. Workaround is:

  #[async_trait(?Send)]
  pub trait Trait<IdT>: Sized {
-     async fn test(id: IdT) -> Self;
+     async fn test(id: IdT) -> Self
+     where
+         IdT: 'async_trait;
  }

playground

EliseZeroTwo commented 3 years ago

that worked, thanks! I'm unsure if you want me to mark this as closed or not so I will just leave it, please feel free to mark it as closed

🖤