我有一个最小的例子:

use std::{future::Future, pin::Pin, thread::JoinHandle, fmt::Debug};

use tokio::runtime::Runtime;

struct Callback<E> {
    f: Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<(), E>> + Send + Sync>> + Send + Sync>,
}

trait Provider {
    fn setup(&self) -> JoinHandle<()>;
}

enum Foo {
    A,
    B
}

trait IntoFoo {
    fn into_foo(&self) -> Foo;
}

impl<E: Debug + IntoFoo> Provider for Callback<E> {
    fn setup(&self) -> JoinHandle<()> {
        std::thread::spawn(move || {
            // Running async function sycnhronously within another thread.
            let rt = Runtime::new().unwrap();
            rt.block_on(handle(Box::new(move || (self.f)())))
              .expect("request loop failed")
          })
    }
}

async fn handle<E: Debug + IntoFoo + 'static>( callback_fn: Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<(), E>> + Send + Sync>> + Send + Sync>) -> Result<(), E> {
    perform(Box::new(move || (callback_fn)())).await
}

pub async fn perform<
  E: Debug + IntoFoo>(
  op: Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<(), E>> + Send + Sync>> + Send + Sync>,
) -> Result<(), E> {
    (op)().await
}

这是一些真实代码的简化版本,我基本上必须在 struct 中传递一个martec回调.这个回调通过多个函数传递.其中一个线程在新生成的线程中调用该函数.

当调用handle时,我得到的错误是在线程派生代码中.

错误是:

error: lifetime may not live long enough
  --> src/indexer/callback.rs:41:41
   |
27 |   fn bootstrap(&self, input: StageReceiver) -> BootstrapResult {
   |                - let's call the lifetime of this reference `'1`
...
41 |         rt.block_on(handle_event(input, Box::new(move |ev: &Event| (self.f)(ev)), &retry_policy, utils))
   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`

我应该如何协调这件事?可以更改 struct 字段类型和任何类似的内容.然而,我必须注意:该函数必须能够被多次调用(它可能在handle中的循环中).

其他一些线程建议在Box中传递异步回调,它们的结果是一个固定的盒装特征对象.这就是为什么我try 了这个系统.

推荐答案

不能在新线程中使用非'static &self引用.

一种 Select 是使用Arc而不是Box,然后克隆它:

use std::{fmt::Debug, future::Future, pin::Pin, sync::Arc, thread::JoinHandle};

use tokio::runtime::Runtime;

struct Callback<E> {
    f: Arc<dyn Fn() -> Pin<Box<dyn Future<Output = Result<(), E>> + Send + Sync>> + Send + Sync>,
}

trait Provider {
    fn setup(&self) -> JoinHandle<()>;
}

enum Foo {
    A,
    B,
}

trait IntoFoo {
    fn into_foo(&self) -> Foo;
}

impl<E: Debug + IntoFoo + 'static> Provider for Callback<E> {
    fn setup(&self) -> JoinHandle<()> {
        let f = Arc::clone(&self.f);
        std::thread::spawn(move || {
            // Running async function sycnhronously within another thread.
            let rt = Runtime::new().unwrap();
            rt.block_on(handle(f)).expect("request loop failed")
        })
    }
}

async fn handle<E: Debug + IntoFoo + 'static>(
    callback_fn: Arc<
        dyn Fn() -> Pin<Box<dyn Future<Output = Result<(), E>> + Send + Sync>> + Send + Sync,
    >,
) -> Result<(), E> {
    perform(callback_fn).await
}

pub async fn perform<E: Debug + IntoFoo>(
    op: Arc<dyn Fn() -> Pin<Box<dyn Future<Output = Result<(), E>> + Send + Sync>> + Send + Sync>,
) -> Result<(), E> {
    op().await
}

您可以在较少的地方使用Arc,而改为使用Box(如果您有需要使用的现有API),方法是将回调包装在它自己的函数中并将其装箱.

Rust相关问答推荐

在‘await’点上使用‘std::同步::Mutex’是否总是会导致僵局?

Arrow RecordBatch as Polars DataFrame

限制未使用的泛型导致编译错误

在Rust中显式装箱受生存期限制的转换闭包

在使用#[NO_STD]时,如何在Rust中收到紧急消息?

不能在一个代码分支中具有不变的自身borrow ,而在另一个代码分支中具有可变的self borrow

零拷贝按步骤引用一段字节

循环访问枚举中的不同集合

你能在Rust中弃用一个属性吗?

返回Result<;(),框<;dyn错误>>;工作

对reqwest提供的这种嵌套JSON struct 进行反序列化

Google chrome 和 Apple M1 中的计算着色器

缺失serde的字段无法设置为默认值

max(ctz(x), ctz(y)) 有更快的算法吗?

实现泛型的 Trait 方法中的文字

为什么 `tokio::join!` 宏不需要 Rust 中的 `await` 关键字?

如何限制通用 const 参数中允许的值?

Rust Serde 为 Option:: 创建反序列化器

类型判断模式匹配panic

为什么这里需要类型注解?