介绍
作为一名Rust的程序员,肯定会注意到Rust社区中的future
运动。一些非常好的库,比如Hyper已经开始使用futures
了,所以我们需要熟悉future的方式。但是如果你像我一样只是一个一般水平的程序员,就会发现,futures的很多概念非常的难理解。虽然有官方文档可以参考,但是官方文档写的非常的简略,并不适合练习。
Futures 简介
和普通的函数不同,Futures更像是个奇怪的函数,因为它们并不会立即运行,这些函数正如其名会在将来运行。使用futures的好处有:性能,优雅,可组合等。但是它的坏处就是比较难理解,应该说非常的难理解。
环境配置
在Cargo.toml 中配置
[dependencies]
futures="*"
tokio-core="*"
futures-await = { git = 'https://github.com/alexcrichton/futures-await' }
在main.rs 中添加
extern crate futures_await as futures;
extern crate tokio_core;
use futures::done;
use futures::prelude::*;
use futures::future::{err, ok};
use tokio_core::reactor::Core;
use std::error::Error;
Rust futures简介
Rust的futures实现仍然没有稳定,所以在学习Rust的futures的时候需要注意,本文提到的内容可以已经过时了。
Rust的futures总是会返回一个Result,意味着你不仅仅需要处理正确的结果,更需要注意处理它返回的错误结果。
现在将下面的函数转化为一个futures风格的函数
fn my_fn() -> Result<u32, Box<Error>> {
Ok(100)
}
简单的实现
fn my_fut() -> impl Future<Item=u32, Error=Box<Error + 'static>> {
ok(100)
}
注意这两个函数实现的方式,返回值不再是Result
,而是 impl Future
, 对于返回值,第二个函数的实现使用的是全部小写的ok
。
因为futures先返回然后才会执行(或者更准确的说,futures是先返回代码,然后等待在将来执行)。所以需要一个run的方法来运行他,在这里使用Reactor
。
let mut reactor = Core::new().unwrap();
let retval = reactor.run(my_fut()).unwrap();
println!("{:?}", retval);
及联执行
Rust中futures最强大的功能就是多个future及联执行。下面我们写一个新的futures的函数
fn my_fut_squared(i: u32) -> impl Future<Item=u32, Error=Box<Error + 'static>> {
ok(i * i)
}
使用及联运行的方式如下:
let chain_future = my_fut().and_then(|retval| my_fut_squared(retval));
let retval2 = reactor.run(chain_future).unwrap();
println!("{:?}", retval2);
代码执行流程如下:
- 首先运行
my_fut
- 当
my_fut
执行完成之后,创建retval
变量,保存到my_fut
执行的结果中 - 运行
my_fut_squared()
函数,并传递retval
变量 - 运行完成
混合使用普通函数和futures函数
可以在futures函数中一起使用普通函数。这个非常有用,因为,并不是所有的函数都需要转化为futures函数。
例如定义一个普通函数
fn fn_plain(i: u32) -> u32 {
i - 50
}
及联函数可以修改如下:
let chain_futures = my_fut().and_then(|retval| {
let retval2 = fn_plain(retval);
my_fut_squared(retval2)
});
let retval3 = reactor.run(chained_future).unwrap();
println!("{:?}", retval3);
如果普通的函数返回的是Result,那么可以使用futures提供的done
函数,他自动的将Result
转化为impl Future
。
例如:
fn my_fn_squared(i: u32) -> Result<u32, Box<Error>> {
Ok(i * i)
}
let chain_future = my_fut().and_then(|retval| {
done(my_fn_squared(retval)).and_then(|retval2| my_fut_squared(retval2))
});
let retval3 = reactor.run(chained_future).unwrap();
println!("{:?}", retval3);
泛型
futures可以直接和泛型结合起来使用,如下:
fn fut_generic_own<A> (a1: A, a2: A) -> impl Future<Item = A, Error = Box<Error>>
where A: std::cmp::PartialOrd
{
if a1 < a2 {
ok(a1)
}else {
ok(a2)
}
}
执行起方式也是相同的
let future = fut_generic_own("Sampo", "jjj");
let retval = reactor.run(future).unwrap();
println!("fut_generic_own == {}", retval);
错误处理
使用and_then
可以非常方便的及联futures,但是对于错误处理统一使用的是Box<Error>
,在and_then 中返回的错误必须相同。下面来实现自定义的Error。
假设有两个Error,ErrorA和ErrorB,实现如下:
#[derive(Debug, Default)]
pub struct ErrorA {}
impl fmt::Display for ErrorA {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ErrorA!");
}
}
impl error::Error for ErrorA {
fn description(&self) -> &str {
"Description for ErrorA"
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
pub struct ErrorB {}
impl fmt::Display for ErrorB {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ErrorB");
}
}
impl error::Error for ErrorB {
fn description(&self) -> &str {
"Description for ErrorB"
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
使用这两个Error
fn fut_error_a() -> impl Future<Item=(), Error=ErrorA> {
err(ErrorA{})
}
fn fut_error_b() -> impl Future<Item=(), Error=ErrorB> {
err(ErrorB())
}
调用者两个函数
let retval = reactor.run(fut_error_a()).unwrap_err();
println!("fut_error_a == {:?}", retval);
let retval = reactor.run(fut_error_b()).unwrap_err();
println!("fut_error_b == {:?}", retval);
结果如下:
fut_error_a == ErrorA
fut_error_b == ErrorB
但是如果使用and_then
做及联则会
let future = fut_error_a().and_then(|| fut_error_b());
编译直接报错:
error[E0271]: type mismatch resolving `<impl futures::Future as futures::IntoFuture>::Error == errors::ErrorA`
--> src/main.rs:166:32
|
166 | let future = fut_error_a().and_then(|_| fut_error_b());
| ^^^^^^^^ expected struct `errors::ErrorB`, found struct `errors::ErrorA`
|
= note: expected type `errors::ErrorB`
found type `errors::ErrorA`
编译的错误结果很明显,及联的函数返回的是ErrorB,所以第一个函数也要返回ErrorB,否则就会报错。
这个错误可以使用map_err
解决。如下
let future = fut_error_a()
.map_err(|e| {
println!("mapping {:?} into ErrorB", e);
ErrorB::default()
})
.and_then(||{fut_error_b()});
let retval = reactor.run(future).unwrap_err();
println!("error chain == {:?}", retval);
现在编译运行如下:
mapping ErrorA into ErrorB
error chain == ErrorB
如果我们希望先运行fut_error_a 再运行fut_error_b 再运行fut_error_a ,可以通过如下转化:
let future = fut_error_a()
.map_err(|_| ErrorB::defaunt())
.and_then(|_| fut_error_b() )
.map_err(|_| ErrorA::default())
.and_then(|_| fut_error_a());
上述代码可以通过 std::convert::From
Trait 简化
impl From<ErrorB> for ErrorA {
fn from(e: ErrorB) -> ErrorA {
ErrorA::default()
}
}
impl From<ErrorA> for ErrorB {
fn from(e: ErrorA) -> ErrorB {
ErrorB::default()
}
}
将map_err 修改为from_err
let future = fut_error_a()
.from_err()
.and_then(|_| fut_error_b())
.from_err()
.and_then(|_| fut_error_a());