4

我遇到了具有 Box 字段和 impl 异步特征的结构的问题。具体来说

error: future cannot be sent between threads safely

看起来发生错误是因为我在 impl 是异步特征的结构中使用了 Box 字段。

以下是我要完成的工作和遇到的问题的一个最小示例。你可以在这里找到它的游乐场。

use async_trait::async_trait;

// My traits
#[async_trait]
pub trait InnerTrait {
    async fn inner_fn(&self) -> Result<(), ()>;
}

#[async_trait]
pub trait OuterTrait {
    async fn outer_fn(&self) -> Result<(), ()>;
}

// My structs
pub struct InnerStruct {}

impl InnerStruct {
    pub fn new() -> impl InnerTrait {
        InnerStruct {}
    }
}

pub struct OuterStruct {
    inner_trait: Box<dyn InnerTrait>,
}

impl OuterStruct {
    pub fn new(inner_trait: Box<dyn InnerTrait>) -> impl OuterTrait {
        OuterStruct { inner_trait }
    }
}

// My trait impls
#[async_trait]
impl InnerTrait for InnerStruct {
    async fn inner_fn(&self) -> Result<(), ()> {
        println!("InnerStruct.inner_fn");
        Ok(())
    }
}

#[async_trait]
impl OuterTrait for OuterStruct {
    async fn outer_fn(&self) -> Result<(), ()> {
        println!("OuterStruct.outer_fn");
        self.inner_trait.inner_fn().await;
        Ok(())
    }
}

#[tokio::main]
async fn main() {
    let inner_trait: Box<dyn InnerTrait> = Box::new(InnerStruct::new());
    let outter_trait: Box<dyn OuterTrait> = Box::new(OuterStruct::new(inner_trait));
    outter_trait.outer_fn().await;
}

首先,我该如何解决这个问题?

其次,我本质上是在尝试为结构编写特征,以便可以轻松地将 impl'ing 结构与其他结构交换出来,类似于我在 Java 中为对象编写接口的方式。我意识到这可能不是我在 Rust 中考虑组件设计的方式,但我是一个初学者,不确定什么是处理基于 trait 的设计的正确方法。如果这不是惯用的 Rust,您将如何重新设计它以使其仍能实现设计目标(在堆栈上下创建和使用特征以允许简单的 impl 交换)?

谢谢。

4

2 回答 2

4

异步 fns 被转换async_trait为以下内容:

fn your_function<'async_trait>(
        &'async_trait self,
    ) -> Pin<Box<dyn std::future::Future<Output = ()> + Send + 'async_trait>>
    where
        Self: Sync + 'async_trait,

注意SendandSync要求,它告诉编译器未来可以在线程之间传输和共享。如果您使用像tokio.

如果您不需要线程安全的期货,则可以使用以下方法避免在异步特征方法上放置Send和限制:Sync#[async_trait(?Send)]

#[async_trait(?Send)]
pub trait InnerTrait {
    async fn inner_fn(&self) -> Result<(), ()>;
}

#[async_trait(?Send)]
impl InnerTrait for InnerStruct {
    async fn inner_fn(&self) -> Result<(), ()> {
        println!("InnerStruct.inner_fn");
        Ok(())
    }
}

另一方面,如果您需要线程安全的期货,您可以将SendSync边界添加到您的结构中,这将满足async_trait未来的要求:

pub struct OuterStruct {
    inner_trait: Box<dyn InnerTrait + Send + Sync>,
}
于 2020-11-26T21:18:43.060 回答
2

某些类型在线程之间发送是安全的,而另一些则不是。这在 Rust 中表达的方式是类型是否实现了Sendtrait。还有另一个特征 ,Sync它标志着一个类型可以安全地通过引用在线程之间共享。

为了表示您的 trait 对象必须可以安全发送,您可以添加一个约束:

pub struct OuterStruct {
    inner_trait: Box<dyn InnerTrait + Send>,
}

但是,OuterStruct包含对InnerTrait对象的引用,因此,如果OuterStruct在线程之间发送,则对内部特征对象的引用同时被共享。因此,for OuterStructto be Send,实现 if InnerTraitmust be Sync

所以它需要是:

pub struct OuterStruct {
    inner_trait: Box<dyn InnerTrait + Sync + Send>,
}

您需要在使用特征对象类型的任何地方添加这些约束。

于 2020-11-26T21:16:48.447 回答