Reputation: 1674
In the Tokio documentation we have this snippet:
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 explanation for this is:
The
lazy
function runs the closure the first time the future is polled. It is used here to ensure thattokio::spawn
is called from a task. Withoutlazy
,tokio::spawn
would be called from outside the context of a task, which results in an error.
I'm not sure I understand this precisely, despite having some familiarity with Tokio. It seems that these two lazy
have slightly different roles, and that this explanation only applies to the first one. Isn't the second call to lazy
(inside the for
loop) just here to convert the closure into a future?
Upvotes: 2
Views: 1993
Reputation: 431469
The purpose of lazy is covered by the 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.
Like a plain closure, it's used to prevent code from being eagerly evaluated. In synchronous terms, it's the difference between calling a function and calling the closure that the function returned:
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();
}
And the parallel for futures 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();
}
With async
/await
syntax, this function should not be needed anymore:
#![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);
}
As you've identified, Tokio's examples use lazy
to:
I view these two aspects of lazy
as effectively the same.
See also:
Upvotes: 6