dtolnay / async-trait

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

problem with default implementation in traits #182

Closed ehsan2003 closed 2 years ago

ehsan2003 commented 2 years ago

the macro does not working properly with default implementation of traits look at this :

use async_trait::async_trait;

#[tokio::main]
async fn main() {
    println!("Hello, world!");
    let b = Baz {};
    t(&b);
}

#[async_trait]
trait Foo {
    async fn foo(&self);
    async fn bar(&self) {
        println!("bar");
    }
}

struct Baz;

#[async_trait]
impl Foo for Baz {
    async fn foo(&self) {
        println!("foo");
    }
}

async fn t(e: &(dyn Foo)) {
    e.foo().await;
    e.bar().await;
    println!("done");
}

expanded result : ( using cargo expand

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use async_trait::async_trait;
fn main() {
    let body = async {
        {
            ::std::io::_print(::core::fmt::Arguments::new_v1(
                &["Hello, world!\n"],
                &match () {
                    _args => [],
                },
            ));
        };
        let b = Baz {};
        t(&b);
    };
    #[allow(clippy::expect_used)]
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .expect("Failed building the Runtime")
        .block_on(body);
}
trait Foo {
    #[must_use]
    #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
    fn foo<'life0, 'async_trait>(
        &'life0 self,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
    where
        'life0: 'async_trait,
        Self: 'async_trait;
    #[must_use]
    #[allow(
        clippy::let_unit_value,
        clippy::type_complexity,
        clippy::type_repetition_in_bounds,
        clippy::used_underscore_binding
    )]
    fn bar<'life0, 'async_trait>(
        &'life0 self,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
    where
        'life0: 'async_trait,
        Self: ::core::marker::Sync + 'async_trait,
    {
        Box::pin(async move {
            let __self = self;
            let _: () = {
                {
                    ::std::io::_print(::core::fmt::Arguments::new_v1(
                        &["bar\n"],
                        &match () {
                            _args => [],
                        },
                    ));
                };
            };
        })
    }
}
struct Baz;
impl Foo for Baz {
    #[allow(
        clippy::let_unit_value,
        clippy::type_complexity,
        clippy::type_repetition_in_bounds,
        clippy::used_underscore_binding
    )]
    fn foo<'life0, 'async_trait>(
        &'life0 self,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
    where
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            let __self = self;
            let _: () = {
                {
                    ::std::io::_print(::core::fmt::Arguments::new_v1(
                        &["foo\n"],
                        &match () {
                            _args => [],
                        },
                    ));
                };
            };
        })
    }
}
async fn t(e: &(dyn Foo)) {
    e.foo().await;
    e.bar().await;
    {
        ::std::io::_print(::core::fmt::Arguments::new_v1(
            &["done\n"],
            &match () {
                _args => [],
            },
        ));
    };
}

the async_trait macro adds a where clause to the end of the trait functions so it breaks object safety: and the compiler result :

warning: the trait Foo cannot be made into an object

  --> src/main.rs:13:14
   |
13 |     async fn bar(&self) {
   |              ^^^
   |
   = note: `#[warn(where_clauses_object_safety)]` on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> src/main.rs:13:14
   |
11 | trait Foo {
   |       --- this trait cannot be made into an object...
12 |     async fn foo(&self);
13 |     async fn bar(&self) {
   |              ^^^ ...because method `bar` references the `Self` type in its `where` clause
   = help: consider moving `bar` to another trait

error[E0277]: `dyn Foo` cannot be shared between threads safely
  --> src/main.rs:29:7
   |
29 |     e.bar().await;
   |       ^^^ `dyn Foo` cannot be shared between threads safely
   |
   = help: the trait `Sync` is not implemented for `dyn Foo`

For more information about this error, try `rustc --explain E0277`.
warning: `async-trait-expand-test` (bin "async-trait-expand-test") generated 1 warning
error: could not compile `async-trait-expand-test` due to previous error; 1 warning emitted
ehsan2003 commented 2 years ago

sorry I found a section explaining about default implementation on docs

ehsan2003 commented 2 years ago

https://docs.rs/async-trait/latest/async_trait/#dyn-traits this is the link to fix for someone who has the same problem