无法放宽 Trait Object 的生命周期

huangapple go评论59阅读模式
英文:

Cannot relax lifetime of Trait Object

问题

我有以下代码:

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{
        // 在这里执行一些异步操作
        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_stuff 中的 self 借用必须是 'static。我需要放宽 worker 字段的生命周期,但我不知道该如何做。如果我将 worker 更改为 Worker 类型而不是 dyn WorkerT,则代码可以工作,但我更希望保持它作为 trait。

我尝试给 Orchestrator 结构体添加生命周期,然后将该生命周期传递给 worker 字段,但然后它说我创建的生命周期需要超越 'static

英文:

I have the following code:

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&lt;dyn WorkerT&lt;Box&lt;dyn Fn(i32) -&gt; Pin&lt;Box&lt;dyn Future&lt;Output = i32&gt; + Send&gt;&gt; + Send + Sync&gt;&gt; + Send + Sync&gt;
}
impl Orchestrator {
async fn other_things(&amp;self, num: i32) -&gt; i32{
// Do some async stuff in here
num+1
}
async fn main_stuff(self: Arc&lt;Self&gt;) {
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&lt;F: Fn(i32) -&gt; Pin&lt;Box&lt;dyn Future&lt;Output = i32&gt; + Send&gt;&gt; + Send + Sync&gt; {
async fn do_work(&amp; self, func: F);
}
struct Worker&lt;F: Fn(i32) -&gt; Pin&lt;Box&lt;dyn Future&lt;Output = i32&gt; + Send&gt;&gt; + Send + Sync&gt; {
phantom: std::marker::PhantomData&lt;F&gt;
}
#[async_trait]
impl&lt;F: Fn(i32) -&gt; Pin&lt;Box&lt;dyn Future&lt;Output = i32&gt; + Send&gt;&gt; + Send + Sync&gt; WorkerT&lt;F&gt; for Worker&lt;F&gt; {
async fn do_work(&amp; 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;
}

With the following error:

error[E0597]: `self` does not live long enough
--&gt; 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 `&#39;static`
29 |     }
|     - `self` dropped here while still borrowed

Currently, because the default lifetime of dyn WorkerT... is &#39;static, it requires that the borrow of self in main_stuff be &#39;static. I need to relax the lifetime of the worker field but I don't know how. If I change worker to be of type 'Workerinstead ofdyn WorkerT...` this works, but I would much prefer keep it as the trait.

I have tried adding a lifetime to the Orchestrator struct and then giving that lifetime to the worker field, but then it says that my created lifetime needs to outlive 'static.

https://play.rust-lang.org/?version=nightly&amp;mode=debug&amp;edition=2018&amp;gist=7f34fa394d706409942659f1d8ce36c0

答案1

得分: 1

你需要在创建闭包之前克隆 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)。

英文:

You need to clone the Arc before creating the closure so that you can give an Arc to the closure. What you have in your code tries to borrow self, which won't outlive the closure. Cloning the Arc solves this issue as the closure can now own its own Arc pointing at the same value.

For example:

    async fn main_stuff(self: Arc&lt;Self&gt;) {
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;
}

The inner clone is still required so that the type of func implements Fn (otherwise it will implement FnOnce due to moving a captured variable into the future).

答案2

得分: 1

    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;
    }
英文:

Solution

    async fn main_stuff(self: Arc&lt;Self&gt;) {
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;
}

Explanation

You are passing a closure that in turns creates async routine. First, the closure it self is static, so it has to take ownership of self. We clone self to slf and add move to the closure, thus closure moved it. Then we have to clone the slf each time and let the async routine own it when it exits the closure.

huangapple
  • 本文由 发表于 2023年2月18日 04:19:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/75488911.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定