rust 无法放宽特征对象的生存期

xoefb8l8  于 2023-02-19  发布在  其他
关注(0)|答案(2)|浏览(93)

下面的代码:

use tokio; // 1.7.1
use futures::future::Future;
use std::pin::Pin;
use futures::FutureExt;
use std::marker::PhantomData;
use std::marker::Send;
use std::sync::Arc;
use async_trait::async_trait;

struct Orchestrator {
    worker: Box<dyn WorkerT<Box<dyn Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync>> + Send + Sync>
}

impl Orchestrator {
    async fn other_things(&self, num: i32) -> i32{
        // Do some async stuff in here
        num+1
    }
    
    async fn main_stuff(self: Arc<Self>) {
        let func = |num: i32| {
            let slf = self.clone();
            async move {
                slf.other_things(num).await
            }.boxed()
        };
        
        self.worker.do_work(Box::new(func)).await;
    }
}

#[async_trait]
trait WorkerT<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> {
    async fn do_work(& self, func: F);
}

struct Worker<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> {
    phantom: std::marker::PhantomData<F>
}

#[async_trait]
impl<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> WorkerT<F> for Worker<F> {
    async fn do_work(& self, func: F) {
        for i in 0..5 {
            func(i).await;
        }
    }
}

#[tokio::main]
async fn main() {
    let orchestrator = Arc::new(Orchestrator { worker: Box::new(Worker { phantom: PhantomData }) });
    orchestrator.main_stuff().await;
}

出现以下错误:

error[E0597]: `self` does not live long enough
  --> src/main.rs:22:23
   |
21 |         let func = |num: i32| {
   |                    ---------- value captured here
22 |             let slf = self.clone();
   |                       ^^^^ borrowed value does not live long enough
...
28 |         self.worker.do_work(Box::new(func)).await;
   |                             -------------- cast requires that `self` is borrowed for `'static`
29 |     }
   |     - `self` dropped here while still borrowed

目前,由于dyn WorkerT...的默认生存期为'static,因此要求main_stuffself的借用为'static。我需要放宽worker字段的生存期,但我不知道如何放宽。如果我将worker更改为"Worker instead of dyn WorkerT ..."类型,这将起作用,但我更愿意把它作为一种特质。
我试过向Orchestrator结构体添加一个生存期,然后将该生存期赋予worker字段,但它说我创建的生存期需要比"static"更长。
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=7f34fa394d706409942659f1d8ce36c0

mzmfm0qo

mzmfm0qo1#

在创建闭包之前,你需要克隆Arc,这样你就可以给闭包一个Arc。你代码中的东西试图借用self,这不会比闭包更有效。克隆Arc解决了这个问题,因为闭包现在可以拥有自己的Arc,指向相同的值。
例如:

async fn main_stuff(self: Arc<Self>) {
        let self2 = self.clone();
        let func = move |num: i32| {
            let self3 = self2.clone();
            async move {
                self3.other_things(num).await
            }.boxed()
        };
        
        self.worker.do_work(Box::new(func)).await;
    }

内部克隆仍然是必需的,以便func类型实现Fn(否则,由于将捕获的变量移到将来,它将实现FnOnce)。

jgovgodb

jgovgodb2#

溶液

async fn main_stuff(self: Arc<Self>) {
        let slf = self.clone();
        let func = move |num: i32| {
            let slf = slf.clone();
            async move { slf.other_things(num).await }.boxed()
        };

        self.worker.do_work(Box::new(func)).await;
    }

解释

你正在传递一个闭包,它反过来创建async例程。首先,闭包it self是静态的,所以它必须拥有self的所有权。我们将self克隆到slf,并向闭包添加move,因此闭包移动了它。然后我们每次都必须克隆slf,并在async例程退出闭包时让它拥有它。

相关问题