指定 Rust 闭包的生命周期

2024-01-31

我在制作执行器/反应器时发现这是一个终身问题。它与 async/Future 无关,并且可以在没有异步糖的情况下重现。

use std::future::Future;

struct Runtime;

fn start_with_runtime<C, F>(closure: C)
where
    C: for<'a> FnOnce(&'a Runtime) -> F,
    F: Future
{
    let rt = Runtime;
    let _future = closure(&rt);
    // block_on(future); 
}

async fn async_main(_rt: &Runtime) {
    // I can use _rt to do async stuff here
}

fn main() {
    start_with_runtime(|rt| { async_main(rt) });
}

我愿意start_with_runtime()运行 future 并提供异步运行时引用作为参数。

它不编译:

error: lifetime may not live long enough
  --> src/main.rs:17:31
   |
17 |     start_with_runtime(|rt| { async_main(rt) });
   |                         ---   ^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |                         | |
   |                         | return type of closure is impl std::future::Future
   |                         has type `&'1 Runtime`

我认为这个问题似乎是因为 Rust 如何推断闭包的生命周期:

https://github.com/rust-lang/rust/issues/58052 https://github.com/rust-lang/rust/issues/58052 :

fn main() {
    let f = |x: &i32| x;
    let i = &3;
    let j = f(i);
}

也不编译:

error: lifetime may not live long enough
 --> src/main.rs:2:23
  |
2 |     let f = |x: &i32| x;
  |                 -   - ^ returning this value requires that `'1` must outlive `'2`
  |                 |   |
  |                 |   return type of closure is &'2 i32
  |                 let's call the lifetime of this reference `'1`

看起来我的关闭签名推断为|&'a Runtime| -> impl Future + 'b从而产生生命周期错误。我觉得给出正确的预期关闭签名会有所帮助,但是我如何提供正确的签名start_with_runtime?

fn start_with_runtime<C>(closure: C)
where
    C: for<'a> FnOnce(&'a Runtime) -> (impl Future + 'a),

不起作用,因为impl Trait这里不允许。

fn start_with_runtime<C,F>(closure: C)
where
    C: for<'a> FnOnce(&'a Runtime) -> F,
    F: Future + 'a

效果不太好,因为'aHRTB 表达之外未知。

如果我知道类型,它就有效:


struct MyType<'a> {
    _rt: &'a Runtime
} 
fn start_with_runtime<C>(closure: C)
where
    C: for<'a> FnOnce(&'a Runtime) -> MyType<'a>,

当你思考了所有的生生世世时,这有点悲伤,但语言无法提供表达这一点的方式。也许 Rust 中有一个技巧可以让这个工作正常进行?


这一问题似乎有两个不同的问题:所需的关系是否可以用 Rust 语法表达,以及它是否适用于闭包类型推断。

让我们从第一个开始。你是对的,这不能仅仅用where条款。为了表达这一点需要添加一个辅助特征

trait BorrowingFn<'a> {
    type Fut: std::future::Future<Output = Something> + 'a;
    fn call(self, arg: &'a Runtime) -> Self::Fut;
}

这允许我们需要写成的界限

    C: for<'a> BorrowingFn<'a>,

并为所有适用的功能提供此特征的全面实现

impl<'a, Fu: 'a, F> BorrowingFn<'a> for F
where
    F: FnOnce(&'a Runtime) -> Fu,
    Fu: std::future::Future<Output = ()> + 'a,
{
    type Fut = Fu;
    fn call(self, rt: &'a Runtime) -> Fu {
        self(rt)
    }
}

()

好的,它可以与异步函数一起使用,但是它可以与需要类型推断的闭包一起使用吗?不幸的是,答案是“不”

error: implementation of `BorrowingFn` is not general enough
  --> src/main.rs:33:5
   |
5  | / trait BorrowingFn<'a> {
6  | |     type Fut: std::future::Future<Output = ()> + 'a;
7  | |     fn call(self, arg: &'a Runtime) -> Self::Fut;
8  | | }
   | |_- trait `BorrowingFn` defined here
...
33 |       start_with_runtime(|rt| async_main(rt)); // however, it does not work with closure type inference :-(
   |       ^^^^^^^^^^^^^^^^^^ implementation of `BorrowingFn` is not general enough
   |
   = note: `[closure@src/main.rs:33:24: 33:43]` must implement `BorrowingFn<'0>`, for any lifetime `'0`...
   = note: ...but `[closure@src/main.rs:33:24: 33:43]` actually implements `BorrowingFn<'1>`, for some specific lifetime `'1`

正在对此进行跟踪rust-lang/rust#70263 https://github.com/rust-lang/rust/issues/70263。编译器还不够聪明,还没有注意到这个闭包需要更高级别的类型。 为了好玩我尝试编译-Z chalk每晚,但它还没有为此做好准备(内部编译器错误)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

指定 Rust 闭包的生命周期 的相关文章

随机推荐