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。