错误处理,目前接触最多的是Option<>,与Result<>,概念就不多说了,具体的可以查一下,也可以查看第一条链接的文章。
先说Option<>这个错误处理机制,一版用于为空的情况,在Rust中称之为None。
举个例子:
let vec = vec![1,2,3];
let a = vec.get(0);
根据动态数组的定义,变量a得到的值其实是个Option<&i32>。因为a有可能为None。
从代码可以看出,a明显为不为None,此时,Rust提供了一个方法,unwrap()。这个方法相当于:你程序员说部位None,那我就相信你,如果为None,那我就panic!,锅全部甩给你,和我没关系。于是就有这样的写法:
let vec1 = vec![1,2,3];
let a: &i32 = vec.get(0).unwrap();
这里明显看出,a的值为1,没什么问题,可以试着将get(0)改为get(4),运行程序会得到报错的结果。如果我们不能保证,得到的这个值是不是为空,我们可以这样写:
let vec1 = vec![1,2,3];
let a = vec.get(0);
if a == None {
println!("a is None");
} else {
println!("a")
}
当然这种写法很低级,当然有更高级的:unwrap_or()。该方法的意思为,如果a为空,则返回一个自定义的值,例如:
let vec = vec![1,2,3];
let a = vec.get(4).unwrap_or(&123)
其中123就是你自定义的值,如果a为空,则a的值就为123。还有跟高级的用法:unwrap_or_else()。该方法可以定义函数:
let vec = vec![1,2,3];
let a: &i32 = vec.get(0).unwrap_or_else(||{
&123
});
当然,可以使用最原始的方法:match,但是个人觉得这种方法太麻烦了。下一个成语就是
Result<T,E>了
Result是个枚举,enum Result<T, E> {Ok(T),Err(E)}(option也是个枚举)。这
包含两个参数,T和E都是泛型类型参数。Result也可以利用unwrap()获取值,但是如果出错则panic。一般的用传统的写法为:
let f = File::open(“hello.txt”);
let f = match f {
Ok(file) => file,
Error(error) => {
println!("Error!");
}
};
这里用match写法,如果当结果为Ok时,返回file。当然也可以用unwrap():
let f = File::open("hello.txt").unwrap();
这里,如果出错,则会panic,和上面说的用法差不多。这里还引入了一个方法:expect:
let f = File::open("hello.txt").expect("Failed to open hello.txt");
expect与unwrap()的使用方式一致,返回文件,或者panic。但是expect用来调用panic!的错误信息将会作为参数传递给expect,而unwrap,使用的是默认的panic信息。我们知道,有时候panic信息很难看,一大堆错误信息,不能马上就定位错误,用expect,就可以根据上下文,写出错误所在。比如上面的代码,我们明显知道是打不开导致的错,这里就可以用expect来表示。这样还有个好处:我们自定义的错误信息将会更容易找到代码中的错误信息来自何处。如果在多处使用unwrap,则需要花更多的时间来分析到底是哪一个unwrap造成看panic,因为所有的unwrap调用都打印相同的信息。
当然,io这个库里有定义错误类型ErrorKind,也可以用这个判断错误类型,举个例子:
let path = Path::new("hello.txt");
let mut file = File::open(&path).map_err(|error| {
if error.kind() == io::ErrorKind::NotFound {
File::create(&path).unwrap()
}else {
println!("{}","some error happened!can not open the target file");
}
});
不仅仅io提供ErrorKind,还有很多三方库提供的,可根据实际情况进行使用。传播错误:使用?进行传播。什么意思呢?就是错误我不处理,将错误传给调用我的人,有点类似于java中的throw。所以,?只能用在返回值是Result的函数中。?号作用是:传播错误,返回的是一个Result,要求这个函数必须有返回,且是Result类型,或者是option类型,或者其他实现了std::ops::Try的类型。一般地,在Rust中,如果确定不了其值(有可能为空),则用Option作为返回值。如果执行什么操作的时候会出现异常,则返回Result。Ok(())返回的就是Result。
我们在写函数的时候,可以将Result作为返回值传递,那问题来了,调用函数的怎么获取值啊?用unwrap啊,笨。
fn read(size: usize) -> Result<usize,i32> {
if size >= 2 {
return Ok(size);
} else {
return Err(12);
}
}
我们可以将需要返回的值这样封装,这样也可以自定义错误,非常好用。当然,Result和Option都有些高级用法,比如说,and_then,map_err等。但是目前还没研究到那么深刻。现在这些知识在写代码是够用的,但是补好看,用了那些高级用法,你的代码看起来会更高大上,更整洁啊,有木有。这个后面再抽时间慢慢研究吧。