Rust编程语言里的future

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust编程语言里的future相关的知识,希望对你有一定的参考价值。

参考技术A 在异步编程里

future代表一个异步计算

它的表示形式就是

在函数前面加上async标签

那么通过await可以等待

运行时的任务去执行future

future背后有poll方法

可以查询future结果

一开始还没输入参数

也就是没执行的时候

自然没有结果

返回的是pending状态

同时poll的上下文中的唤醒器

会被拷贝一份到poll里

一旦future就绪

poll里的唤醒器

来唤醒wake方法就是

通知运行时这个future

可以执行了

来个任务来执行future吧

如果运行结束就返回结果

没有结束就返回pending状态

还可以继续poll查询状态

但是只有最近一次的poll的唤醒器

会被通知去执行

可见调用future的时候

它是惰性的

它本身不会去执行

它依赖唤醒器的wake方法

去通知有进度了

poll就获取结果

这就避免了无限

循环的去查询future

其它编程语言里的异步是怎么运行的你,你知道吗,欢迎交流

指定 Rust 闭包的生命周期

【中文标题】指定 Rust 闭包的生命周期【英文标题】:Specify Rust closures lifetime 【发布时间】:2020-12-10 12:13:03 【问题描述】:

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

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() 运行未来并提供异步运行时引用作为参数。

它不编译:

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:

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`

看起来我的闭包签名被推断为|&amp;'a Runtime| -&gt; 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

效果不佳,因为'a 在 HRTB 表达式之外是未知的。

如果我知道类型就可以了:


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

当你思考了所有的人生但语言并没有提供表达这一点的方式时,这有点令人难过。也许在 rust 中有一个技巧可以使这项工作发挥作用?

【问题讨论】:

我认为你不能只提取 start_with_runtime 到 main 吗?因为这应该可以工作,没有任何明确的生命周期。 start_with_runtime 应该在 crate 中并由应用程序使用(例如,对应用程序隐藏运行时构造)。这是应用程序可以let rt = Runtime::new(); rt.run(|rt| my_async_fn(rt)); 的一种备份计划 异步函数的返回类型确实是captures all argument lifetimes。它必须这样做,因为每当异步函数等待其他未来时,参数都需要存储在 Future 中。 接收像Rc&lt;Runtime&gt; 这样的共享指针而不是对运行时的引用对你有用吗? 我想Rc&lt;&gt; 会起作用,但这是开销,对我来说不喜欢权利所有权模型。 【参考方案1】:

在这个问题中似乎有两个不同的问题:所需的关系能否用 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)
    

(playground)

好的,它适用于异步函数,但它是否适用于需要类型推断的闭包?不幸的是,答案是“不”

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 中进行跟踪。编译器还不够聪明,还没有注意到这个闭包需要更高级别的类型。 为了好玩,我尝试在 Nightly 上使用 -Z chalk 进行编译,但它还没有准备好(内部编译器错误)。

【讨论】:

看起来很聪明的方法!我认为这对我来说是一个很好的权衡(它不涉及堆,但要求 FnOnce 是一个函数,而不是闭包,这还不错)。【参考方案2】:

抱歉,这是语言的限制。您只能指定具体类型的生命周期。一种解决方法是使用特征对象类型。

fn start_with_runtime<C, F, T>(closure: C)
where
    C: for<'a> FnOnce(&'a Runtime) -> Pin<Box<dyn Future<Item = T> + Send + 'a>>,

    let rt = Runtime;
    let _future = closure(&rt);
    // block_on(future); 

【讨论】:

通过一些修复,我能够编译这个版本。在这种情况下,关闭看起来像start_with_runtime(|rt| Box::pin(async_main(rt)) );,所以这是一种可能的解决方法。但是我发现 Tanriol 的解决方案看起来更可取,因为它不需要堆和运行时多态性。

以上是关于Rust编程语言里的future的主要内容,如果未能解决你的问题,请参考以下文章

Rust编程语言里的B树map

Rust定时组件async-rs / futures-timer实现原理

Rust futures: async fn 中的 thread::sleep 和阻塞调用

并发编程Future模式及JDK中的实现

小学生容易理解的Rust的FutureLinux的Epoll,为什么程序员这么难学?高并发背后的殊途同归!

指定 Rust 闭包的生命周期