本文简介
9.1 通用概念
设计程序要考虑正常流程与非正常情况,我们对非正常情况分成三类:
- 失败
- 程序正常运行应具有的前提条件不对
- 错误
- 程序运行中遇到了预估到的问题
- 异常
- 程序运行中出现的各种没想到的问题
9.2 消除失败
Rust 用两种方法来消除失败:
- 类型系统
- 断言
Rust可以在编译期就对函数参数进行类型检查,消除类型不匹配的失败;对于那些在运行期才会出现的失败,我们使用断言应对。比如
pub fn insert(&mut self, index: usize, element: T) { let len = self.len(); assert!(index <= len); ... }
assert
就是一种快速失败策略,尽早暴露问题。所以assert
还可以输出自定义失败信息:assert!(x, "x不为真");
assert
会占用CPU时间,所以可以使用debug_assert
只在调试模式下起作用
程序运行的条件包括:
- 前置条件
- 后置条件
- 前后不变
9.3 分层处理错误
Rust处理错误采用了分层处理的方式,应对工具是:
Option<T>
:有无型错误Result<T, E>
:通用错误panic!()
abort
9.3.1 有无型错误 Option
Option<T>
包含Some(T)
和None
两个变体。比如通过Option<T>
方式找到最短名字的程序
fn main() {
assert_eq!(show_shortest(vec!["Uku", "AiQi"]), "Uku");
assert_eq!(show_shortest(Vec::new()), "Not Found");
}
这里show_shortest
函数内部通过match
来处理get_shotest
函数返回的Option<&str>
结果,我们可以认为show_shortest
处理了有无型错误
fn show_shortest(names: Vec<&str>) -> &str {
match get_shortest(names) {
Some(shortest) => shortest,
None => "Not Found",
}
}
unwrap系方法
通过match
操作Option<T>
来得到内部的T
在编程中太频率,所以Rust给出了一个语法糖,让处理过程更优雅,这就是:
unwrap()
:必须解包,不能就panicexpect("Not Found")
:必须解包,不能就panic,但带自定义的信息unwrap_or("Not Found")
:解包或返回自定义结果unwrap_or_else(|| "Not Found")
:解包或执行闭包
map方法
通过match
操作Option<T>
来得到一个Option<U>
的情况,在编译中也很频率,为此Rust给出了一个map
的语法糖,这就是:
map(f)
:映射到函数里Option<T>.map(f) ->
Option<f(T)>`x.map(|v| v.len())
map_or(v, f)
:为None指定默认值v
x.map_or(52, |v| v.len())
map_or_else(g, f)
:为None指定默认函数g
x.map_or_else(|| 2*k, |v| v.len())
map(f)
相当于:
match self {
Some(x) => Some(f(x)),
None => None,
}
比如:get_shortest(names).map(|name| name.len())
and_then方法
map(f).map(g).map(h)
的一个隐含前提是f(x) -> T
而不是f(x) -> Some(T)
,否则链式调用会变成Some(Some(Some(r)))
。为了处理f(x)->Option<T>
的情况,因为像log()
这类的函数,其结果是可能为None
的,所以它返回值是Option<T>
类型,Rust给出了and_then()
方法,它相当于:
match self {
Some(x) => f(x),
None => None,
}
也就是对于函数结果是Option
的函数,使用and_then
。比如inverse()
和double()
都返回T
,而log
返回Option
,则:
Option::from(number).map(inverse)
.map(double).and_then(log)
.map(square).and_then(sqrt)
9.3.2 通用的错误 Result<T, E>
Rust中Result<T, E>
的定义:
pub enum Result<T, E> {
Ok(T), Err(E)
}
Rust用对于Result
类型的值,如果是Ok(value)
正常的话不处理,有Err(ParseIntErr{kind: InvalidDigit})
错误则引发panic,并且把Err(E)
中的E
输出
Rust对于Result
类型,也有unwrap
系方法,以及map
和and_then
的组合算子来处理。这里与Option<T>
不同的是,Result
多了一个Err(e)
,这个Err
类型可以由我们自己在函数声明中指定,比如:
fn square(numstr: &str) -> Result<i32, ParseIntError> {
numstr.parse::<i32>().map(|n| n.pow(2));
}
技巧,用
type
简化:type ParseResult<T> = Result<T, ParseIntError> fn square(numstr: &str) -> ParseResult<i32>;