We're trying to create a pointer to a trait object and getting a warning that the trait cannot be made into an object.
use async_trait::async_trait;
use std::sync::{Arc, Weak};
fn main() {
println!("Hello, world!");
}
pub type MyTraitPtr = Arc<dyn MyTrait>;
#[async_trait]
pub trait MyTrait {
async fn foo(&self) {}
}
pub struct Parent {
child: MyTraitPtr,
}
This produces the following warning:
warning: the trait `MyTrait` cannot be made into an object
--> src/main.rs:14:14
|
14 | async fn foo(&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:14:14
|
13 | pub trait MyTrait {
| ------- this trait cannot be made into an object...
14 | async fn foo(&self) {}
| ^^^ ...because method `foo` references the `Self` type in its `where` clause
= help: consider moving `foo` to another trait
Full code: https://github.com/lunar-mining/trait_ptr
Update: if we add Sync to the Trait definition then it compiles, like so:
use std::sync::{Arc, Weak};
fn main() {
println!("Hello, world!");
}
pub type MyTraitPtr = Arc<dyn MyTrait>;
#[async_trait]
pub trait MyTrait: Sync {
async fn foo(&self) {}
}
pub struct Parent {
child: MyTraitPtr,
}
However if the trait's methods take an Arc reference to Self, then it does not compile:
use async_trait::async_trait;
use std::sync::{Arc, Weak};
fn main() {
println!("Hello, world!");
}
pub type MyTraitPtr = Arc<dyn MyTrait>;
#[async_trait]
pub trait MyTrait: Sync {
async fn foo(self: Arc<Self>) {}
}
pub struct Parent {
child: MyTraitPtr,
}
Discussion of the problem here: https://github.com/rust-lang/rust/issues/51443
CodePudding user response:
You can implement that by moving the implementation out of the trait:
use async_trait::async_trait;
use std::sync::{Arc, Weak};
pub type MyTraitPtr = Arc<dyn MyTrait>;
#[async_trait]
pub trait MyTrait {
async fn foo(&self);
}
impl dyn MyTrait {
async fn foo(&self) { }
}
pub struct Parent {
child: MyTraitPtr,
}
You can also implement that directly to Arc:
pub type MyTraitPtr = Arc<dyn MyTrait>;
#[async_trait]
pub trait MyTrait: Sync Send {
async fn foo(&self);
}
#[async_trait]
impl MyTrait for MyTraitPtr {
async fn foo(&self) { }
}
pub struct Parent {
child: MyTraitPtr,
}
CodePudding user response:
From async-trait
docs:
Dyn traits
Traits with async methods can be used as trait objects as long as they meet the usual requirements for dyn – no methods with type parameters, no self by value, no associated types, etc.
...
The one wrinkle is in traits that provide default implementations of async methods. In order for the default implementation to produce a future that is Send, the async_trait macro must emit a bound of
Self: Sync
on trait methods that take&self
and a boundSelf: Send
on trait methods that take &mut self. An example of the former is visible in the expanded code in the explanation section above.If you make a trait with async methods that have default implementations, everything will work except that the trait cannot be used as a trait object. Creating a value of type
&dyn Trait
will produce an error that looks like this:error: the trait `Test` cannot be made into an object --> src/main.rs:8:5 | 8 | async fn cannot_dyn(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For traits that need to be object safe and need to have default implementations for some async methods, there are two resolutions. Either you can add Send and/or Sync as supertraits (Send if there are
&mut self
methods with default implementations, Sync if there are&self
methods with default implementations) to constrain all implementors of the trait such that the default implementations are applicable to them:#[async_trait] pub trait ObjectSafe: Sync { // added supertrait async fn can_dyn(&self) {} } let object = &value as &dyn ObjectSafe;
or you can strike the problematic methods from your trait object by bounding them with
Self: Sized
:#[async_trait] pub trait ObjectSafe { async fn cannot_dyn(&self) where Self: Sized {} // presumably other methods } let object = &value as &dyn ObjectSafe;
Arc
is considered the same as mutable references in this regard, and requires a Send
bound. See also the bug report I posted to async-trait
.