Rust 通用结果方法

示例

use std::io::{Read, Result as IoResult};
use std::fs::File;

struct Config(u8);

fn read_config() -> IoResult<String> {
    let mut s = String::new();
    let mut file = File::open(&get_local_config_path())
        // 如果Result为Err,则调用or_else闭包。
        .or_else(|_| File::open(&get_global_config_path()))?;
    // 注意:在`or_else`中,闭包应该返回带有匹配项的Result
    //       确定类型,而在`and_then`中,返回的Result应该有一个
    //       匹配的Err类型。
    let _ = file.read_to_string(&mut s)?;
    Ok(s)
}

struct ParseError;

fn parse_config(conf_str: String) -> Result<Config, ParseError> {
    // 解析配置字符串...
    if conf_str.starts_with("bananas") {
        Err(ParseError)
    } else {
        Ok(Config(42))
    }
}

fn run() -> Result<(), String> {
    //注意:此函数的错误类型为字符串。我们使用下面的map_err
    //       将错误值转换为String类型
    let conf_str = read_config()
        .map_err(|e| format!("Failed to read config file: {}", e))?;
    // 注意:我们可以使用`and_then`来包装let而不是上面的`?`。
    //       表达式如下。
    let conf_val = parse_config(conf_str)
        .map(|Config(v)| v / 2) // map可用于仅映射Ok值
        .map_err(|_| "无法解析配置字符串!".to_string())?;

    // 跑...

    Ok(())
}

fn main() {
    match run() {
        Ok(_) => println!("Bye!"),
        Err(e) => println!("Error: {}", e),
    }
}

fn get_local_config_path() -> String {
    let user_config_prefix = "/home/user/.config";
    // 获取用户配置目录的代码
    format!("{}/my_app.rc", user_config_prefix)
}

fn get_global_config_path() -> String {
    let global_config_prefix = "/etc";
    // 获取全局配置目录的代码
    format!("{}/my_app.rc", global_config_prefix)
}

如果配置文件不存在,则输出:

Error: Failed to read config file: No such file or directory (os error 2)

如果解析失败,则输出:

Error: 无法解析配置字符串!

注意:随着项目的发展,使用这些基本方法(文档)来处理错误会很麻烦,而又不会丢失有关错误的来源和传播路径的信息。同样,过早地将错误转换为字符串以处理多种错误类型绝对是一个坏习惯,如上所述。更好的方法是使用板条箱error-chain。