繁体   English   中英

Rust 特征警告:方法在其“where”子句中引用了“Self”类型

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

我们正在尝试创建一个指向特征 object 的指针,并收到一条警告,指出该特征不能变成 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,
}

这会产生以下警告:

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

完整代码: https://github.com/lunar-mining/trait_ptr

更新:如果我们将 Sync 添加到 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) {}
}

pub struct Parent {
    child: MyTraitPtr,
}

但是,如果特征的方法采用对 Self 的 Arc 引用,则它不会编译:

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

问题讨论在这里: https://github.com/rust-lang/rust/issues/51443

您可以通过将实现移出特征来实现它:

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

您也可以直接将其实现到 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,
}

来自async-trait文档

动态特性

具有异步方法的特征可以用作特征对象,只要它们满足 dyn 的通常要求——没有带有类型参数的方法、没有按值的自我、没有关联类型等。

...

一个问题在于提供异步方法默认实现的特征。 为了让默认实现产生一个发送的未来,async_trait 宏必须发出一个 Self 的边界:在采用&self的特征方法上Self: Sync和一个绑定的Self: Send 前者的示例在上面解释部分的扩展代码中可见。

如果您使用具有默认实现的异步方法创建一个特征,除了该特征不能用作特征 object 之外,一切都会正常工作。创建&dyn Trait类型的值将产生如下所示的错误:

 error: the trait `Test` cannot be made into an object --> src/main.rs:8:5 | 8 | async fn cannot_dyn(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

对于需要 object 安全并且需要对某些异步方法具有默认实现的特征,有两种解决方案。 您可以将 Send 和/或 Sync 添加为超特征(如果有默认实现的&mut self方法则发送,如果有默认实现的&self方法则同步)以约束特征的所有实现者,以便默认实现适用于它们:

 #[async_trait] pub trait ObjectSafe: Sync { // added supertrait async fn can_dyn(&self) {} } let object = &value as &dyn ObjectSafe;

或者你可以通过将它们与Self: Sized绑定来从你的特征 object 中删除有问题的方法:

 #[async_trait] pub trait ObjectSafe { async fn cannot_dyn(&self) where Self: Sized {} // presumably other methods } let object = &value as &dyn ObjectSafe;

Arc在这方面被认为与可变引用相同,并且需要Send绑定。 另请参阅我发布到async-trait的错误报告

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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