dtolnay / async-trait

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

Problem with declarative macros #104

Closed davidli2010 closed 4 years ago

davidli2010 commented 4 years ago

Hello there. I have problem with declarative macros. The code is simplified as below:

use async_trait::async_trait;

#[async_trait]
trait T1 {
    async fn id(&self) -> i32;
}

macro_rules! impl_t1 {
    ($ty: ty, $id: expr) => {
        #[async_trait]
        impl T1 for $ty {
            async fn id(&self) -> i32 {
                $id
            }
        }
    };
}

struct Foo;

impl_t1!(Foo, 1);

fn main() {}

rust compiler generates an error:

error[E0424]: expected value, found module `self`
  --> src/main.rs:10:9
   |
1  | / use async_trait::async_trait;
2  | |
3  | | #[async_trait]
4  | | trait T1 {
...  |
9  | |     ($ty: ty, $id: expr) => {
10 | |         #[async_trait]
   | |         ^^^^^^^^^^^^^-
   | |_________|____________|
   |           |            this function doesn't have a `self` parameter
   |           `self` value is a keyword only available in methods with a `self` parameter
...
21 |   impl_t1!(Foo, 1);
   |   ----------------- in this macro invocation
   |
   = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0424`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.
dtolnay commented 4 years ago

This is a compiler bug (https://github.com/rust-lang/rust/issues/43081). You can work around it by writing the decl macro as:

macro_rules! impl_t1 {
    ($ty:tt, $($id:tt)*) => {
        #[async_trait]
        impl T1 for $ty {
            async fn id(&self) -> i32 {
                $($id)*
            }
        }
    };
}
taiki-e commented 4 years ago

This will be fixed in #105.

davidli2010 commented 4 years ago

This is a compiler bug (rust-lang/rust#43081). You can work around it by writing the decl macro as:

macro_rules! impl_t1 {
    ($ty:tt, $($id:tt)*) => {
        #[async_trait]
        impl T1 for $ty {
            async fn id(&self) -> i32 {
                $($id)*
            }
        }
    };
}

It works. Thank you!