Skip to content

33.2 组合错误类型

利用代数类型系统做错误处理的另外一大好处是可组合性(composability)。比如 Result 类型有这样的一系列成员方法:


rust
fn map<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> U
fn map_err<F, O>(self, op: O) -> Result<T, F> where O: FnOnce(E) -> F
fn and<U>(self, res: Result<U, E>) -> Result<U, E>
fn and_then<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> Result<U, E>
fn or<F>(self, res: Result<T, F>) -> Result<T, F>
fn or_else<F, O>(self, op: O) -> Result<T, F> where O: FnOnce(E) -> Result<T, F>

这些方法的签名稍微有点复杂,涉及许多泛型参数。它们之间的区别也就表现在方法签名中。我们可以用下面的方式去掉语法干扰之后,来阅读函数签名,从而理解这些方法之间的区别:

方法方法类型参数类型
mapResult<T, E> -> Result<U, E>T -> U
map_errResult<T, E> -> Result<T, F>E -> F
andResult<T, E> -> Result<U, E>Result<U, E>
and_thenResult<T, E> -> Result<U, E>T -> Result<U, E>
orResult<T, E> -> Result<T, F>Result<T, F>
or_elseResult<T, E> -> Result<T, F>E -> Result<T, F>

通过这个表格的对比,我们可以很容易看出它们之间的区别。比如mapand_then的主要区别是闭包参数:map的参数是做的T -> U的转换,而and_then的参数是T -> Result的转换。Option 类型也有类似的对应的方法,读者可以自己建一个表格,对比一下这些方法签名之间的区别。

下面用一个示例演示一下这些组合函数的用法:


rust
use std::env;

fn double_arg(mut argv: env::Args) -> Result<i32, String> {
    argv.nth(1)
        .ok_or("Please give at least one argument".to_owned())
        .and_then(|arg| arg.parse::<i32>().map_err(
            |err| err.to_string()))
        .map(|n| 2 * n)
}

fn main() {
    match double_arg(env::args()) {
        Ok(n) => println!("{}", n),
        Err(err) => println!("Error: {}", err),
    }
}

Released under the MIT License