1 个不稳定版本
使用旧的 Rust 2015
0.1.0 | 2018年1月21日 |
---|
#1169 in 异步
24KB
future-by-example
本文档旨在帮助读者快速开始使用 Rust 的 Future
。其他一些有用的阅读材料包括
Future
Future
trait 来自 futures
表示一个可能成功或失败的异步操作,无论哪种情况都会产生一个值。它类似于异步版本的 Result
。本文档假定读者熟悉 Result
,这在《Rust 编程语言》的第二版中有介绍。
关于 Future
最常见的问题之一似乎是,“我如何从中获取值?” 完成此操作的最简单方法是调用 wait
方法。这将在当前线程中运行 Future
,在它完成之前阻塞所有其他工作。
这通常不是运行 Future
的最佳方式,因为除非 Future
完成,否则无法进行其他任何工作,这完全违背了最初使用异步编程的目的。然而,在单元测试、调试或在简单应用程序的顶层时,它可能很有用。
请参阅有关反应器部分的章节,了解运行 Future
的更好方法。
extern crate futures;
extern crate future_by_example;
fn main() {
use futures::Future;
use future_by_example::new_example_future;
let future = new_example_future();
let expected = Ok(2);
assert_eq!(future.wait(), expected);
}
可以使用与 Result
类似的许多函数修改 Future
,例如 map
、map_err
和 then
。以下是 map
extern crate futures;
extern crate future_by_example;
fn main() {
use futures::Future;
use future_by_example::new_example_future;
let future = new_example_future();
let mapped = future.map(|i| i * 3);
let expected = Ok(6);
assert_eq!(mapped.wait(), expected);
}
与 Result
一样,两个 Future
可以使用 and_then
和 or_else
进行组合
extern crate futures;
extern crate future_by_example;
fn main() {
use futures::Future;
use future_by_example::{new_example_future, new_example_future_err, ExampleFutureError};
let good = new_example_future();
let bad = new_example_future_err();
let both = good.and_then(|good| bad);
let expected = Err(ExampleFutureError::Oops);
assert_eq!(both.wait(), expected);
}
Future
还有很多函数在 Result
中没有对应物。因为我们正在讨论异步编程,现在我们必须选择是想顺序地(一个接一个地)运行两个独立的操作,还是同时运行(并行地)。
例如,为了获取两个独立 Future
的结果,我们可以使用 and_then
来按顺序执行它们。然而,这种策略很愚蠢,因为我们一次只在一个 Future
上取得进展。为什么不同时运行它们呢?
Future::join
创建一个新的 Future
,其中包含两个其他 Future
的结果。重要的是,这两个输入 Future
可以同时取得进展。新的 Future
仅在两个输入 Future
都完成时才完成。还有 join3
、join4
和 join5
用于连接更多数量的 Future
。
extern crate futures;
extern crate future_by_example;
fn main() {
use futures::Future;
use futures::future::ok;
use future_by_example::new_example_future;
let future1 = new_example_future();
let future2 = new_example_future();
let joined = future1.join(future2);
let (value1, value2) = joined.wait().unwrap();
assert_eq!(value1, value2);
}
而 join
在两个 Future
都完成时才完成,select
则返回两个 Future
中先完成的一个。这在实现超时等功能时很有用。 select2
与 select
类似,只不过两个 Future
可以有不同的值类型。
创建一个 Future
许多库返回 Future
用于异步操作,如网络调用。有时你可能想创建自己的 Future
。从头实现 Future
很困难,但还有其他创建未来(future)的方法。
你可以使用 ok
函数轻松地从已存在的值创建一个 Future
。还有类似的 err
和 result
方法。
extern crate futures;
fn main() {
use futures::Future;
use futures::future::ok;
// Here I specify the type of the error as (); otherwise the compiler can't infer it
let future = ok::<_, ()>(String::from("hello"));
assert_eq!(Ok(String::from("hello")), future.wait());
}
未来和类型
使用 Future
的工作往往会产生复杂类型。例如,以下表达式的完整类型实际上是
futures::Map<
futures::Map<
futures::Join<
futures::FutureResult<u64, ()>,
futures::FutureResult<u64, ()>
>,
[closure@src/lib.rs:...]>,
[closure@src/lib.rs:...]
>
也就是说,对于每次转换,我们都会给我们的 Future
类型添加一个额外的层!这有时可能会令人困惑。特别是,写出类型的方式可能会很脆弱或冗长。
为了帮助 Rust 编译器进行类型推断,我们在下面指定了 expected
的类型。这比写出完整的类型要简洁得多,并且添加另一个操作不会破坏编译。
extern crate futures;
fn main() {
use futures::future::ok;
use futures::Future;
let expected: Result<u64, ()> = Ok(6);
assert_eq!(
ok(5).join(ok(7)).map(|(x, y)| x + y).map(|z| z / 2).wait(),
expected
)
}
或者,我们可以使用 _
让 Rust 编译器为我们推断类型。
extern crate futures;
fn main() {
use futures::future::ok;
use futures::Future;
use futures::Map;
let expected: Result<_, ()> = Ok(6);
let twelve: Map<_, _> = ok(5).join(ok(7)).map(|(x, y)| x + y);
assert_eq!(twelve.map(|z| z / 2).wait(), expected)
}
Rust 要求在函数签名中指定所有类型。
对于返回 Future
的函数,一种实现方式是在函数签名中指定完整的返回类型。然而,指定确切的类型可能会很冗长、脆弱和困难。
我们希望能够定义一个像这样的函数
fn make_twelve() -> Future<Item=u64, Error=()> {
unimplemented!()
}
然而,编译器不喜欢这样
error[E0277]: the trait bound `futures::Future<Item=u64, Error=()>: std::marker::Sized` is not satisfied
--> src/lib.rs:119:13
|
119 | let twelve = make_twelve();
| ^^^^^^ `futures::Future<Item=u64, Error=()>` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `futures::Future<Item=u64, Error=()>`
= note: all local variables must have a statically known size
这可以通过将返回类型包装在一个 Box
中来解决。有一天,这将通过目前尚不稳定的 impl Trait 功能以更优雅的方式解决。
extern crate futures;
fn main() {
use futures::Future;
use futures::future::ok;
fn make_twelve() -> Box<Future<Item=u64, Error=()>> {
ok(5).join(ok(7)).map(|(x, y)| x + y).boxed()
}
let twelve = make_twelve();
assert_eq!(twelve.map(|z| z / 2).wait(), Ok(6))
}
与函数不同,闭包不需要在其签名中显式定义所有类型,因此不需要包裹在一个 Box
中。
extern crate futures;
fn main() {
use futures::Future;
let make_twelve = || {
use futures::future::ok;
// We don't need to put our `Future` inside of a `Box` here.
ok(5).join(ok(7)).map(|(x, y)| x + y)
};
let expected: Result<u64, ()> = Ok(6);
let twelve = make_twelve();
assert_eq!(twelve.map(|z| z / 2).wait(), expected)
}
运行未来的更强大方式
将多个 Futures
组合成一个单独的 Future
并调用其上的 wait
方法是一个简单易行的方法,只要您一次只运行一个 Future
。然而,如果您一次只运行一个 Future
,可能一开始就无需使用 futures
crate!futures
crate 承诺能够高效地处理许多并发任务,让我们看看它是如何实现的。
tokio-core
crate 包含一个名为 Core
的结构体,它可以并发运行多个 Future
。调用 Core::run
来运行一个 Future
并返回其值。与 Future::wait
不同,它允许 Core
在执行 run
时,同时执行其他 Future
对象。在 Core::run
中的 Future
是主事件循环,它可以通过调用 Handle::spawn
请求运行新的 Future
。请注意,通过 spawn
运行的 Future
无法返回值;它们的存在只是为了执行副作用。
extern crate futures;
extern crate tokio_core;
fn main() {
use tokio_core::reactor::Core;
use futures::future::lazy;
let mut core = Core::new().unwrap();
let handle = core.handle();
let future = lazy(|| {
handle.spawn(lazy(|| {
Ok(()) // Ok(()) implements FromResult
}));
Ok(2)
});
let expected: Result<_, ()> = Ok(2usize);
assert_eq!(core.run(future), expected);
}
许可:MIT/Apache-2.0
依赖项
~6MB
~94K SLoC