簡體   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