futures::future::lazy 有什么用?

What is futures::future::lazy for?

Tokio documentation 中我们有这个片段:

extern crate tokio;
extern crate futures;

use futures::future::lazy;

tokio::run(lazy(|| {
    for i in 0..4 {
        tokio::spawn(lazy(move || {
            println!("Hello from task {}", i);
            Ok(())
        }));
    }

    Ok(())
}));

对此的解释是:

The lazy function runs the closure the first time the future is polled. It is used here to ensure that tokio::spawn is called from a task. Without lazy, tokio::spawn would be called from outside the context of a task, which results in an error.

尽管对 Tokio 有一些了解,但我不确定我是否准确理解了这一点。看来这两个lazy的作用略有不同,这个解释只适用于第一个。第二次调用 lazy(在 for 循环内)不就是为了将闭包转换为 future 吗?

lazy 的目的 包含在 documentation for lazy:

Creates a new future which will eventually be the same as the one created by the closure provided.

The provided closure is only run once the future has a callback scheduled on it, otherwise the callback never runs. Once run, however, this future is the same as the one the closure creates.

就像普通闭包一样,它用于防止代码被急切求值。用同步术语来说,就是调用函数和调用函数返回的闭包的区别:

fn sync() -> impl FnOnce() {
    println!("This is run when the function is called");
    
    || println!("This is run when the return value is called")
}

fn main() {
    let a = sync();
    println!("Called the function");
    a();
}

和期货 0.1 的并行:

use futures::{future, Future}; // 0.1.27

fn not_sync() -> impl Future<Item = (), Error = ()> {
    println!("This is run when the function is called");

    future::lazy(|| {
        println!("This is run when the return value is called");
        Ok(())
    })
}

fn main() {
    let a = not_sync();
    println!("Called the function");
    a.wait().unwrap();
}

使用async/await语法,应该不再需要这个函数了:

#![feature(async_await)] // 1.37.0-nightly (2019-06-05)

use futures::executor; // 0.3.0-alpha.16
use std::future::Future;

fn not_sync() -> impl Future<Output = ()> {
    println!("This is run when the function is called");

    async {
        println!("This is run when the return value is called");
    }
}

fn main() {
    let a = not_sync();
    println!("Called the function");
    executor::block_on(a);
}

如您所见,Tokio 的示例使用 lazy 来:

  • 确保闭包中的代码仅运行来自执行器内部。
  • 确保闭包 运行 作为未来

我认为 lazy 的这两个方面实际上是一样的。

另请参阅: