简体   繁体   中英

Rust Trait warning: method references the `Self` type in its `where` clause

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

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,
}

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 bound Self: 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM