Panic
类似于 JS 中的 throw new Error ,在 Rust 中是调用 panic! 这个宏抛出异常
fn main() {panic!("error");}
如果加上 RUST_BACKTRACE的环境变量执行 Rust 程序,当抛出异常时会把详细的堆栈信息打印出来
$ RUST_BACKTRACE=1 cargo run
Result
类似于 Option<T> ,针对错误信息,Rust 提供了一个叫 Result 的枚举。
enum Result<T, E> {Ok(T),Err(E),}
比如文件操作,返回的就是 Result 这个枚举,可以通过 match 获取到正确的结果。
use std::fs::File;fn main() {let f = File::open("src/text.txt");let mut f = match f {Ok(file) => file,Err(e) => panic!("read file filed, {:?}", e),};let mut str = String::new();let r = f.read_to_string(&mut str);match r {Ok(r) => {println!("content is {}, size is {}", str, r);},Err(e) => panic!("read file filed, {:?}", e),}}
unwrap & expect
按照枚举的用法,如果总是要 match 的话太繁琐,所以 Rust 给 Result枚举提供了一些好用的方法,可以通过 unwrap 或者 expect来简写逻辑。
use std::fs::File;fn main() {// 其实 fs 提供了更简单的方法,可以直接 fs::read_to_string("src/text.txt")。// 这里 demo 先不这么写了。let mut f = File::open("src/text.txt").unwrap();let mut str = String::new();let r = f.read_to_string(&mut str).expect("read file filed");println!("content is {}, size is {}", str, r);}
unwrap 或者 expect都相当于帮做了 match 的逻辑,如果成功返回结果,失败则抛出异常,两个函数的不同之处就是 unwrap 不接收自定义的错误信息,但是 expect会接收。
自己写的函数也可以返回 Result 这个枚举。跟 Option<T> 一样,Result 的枚举可以直接使用( Ok 和 Err )
use rand::{thread_rng, Rng};fn main() {let f = test_result().unwrap();println!("{}", f);}fn test_result() -> Result<String, String> {let rand: u8 = thread_rng().gen_range(0..10);if rand > 5 {Ok("123")} else {Err("123")}}
除了使用 unwrap,还可以用 unwrap_or_else 来对错误进行更进一步的处理,unwrap_or_else支持传入一个回调函数,也就是出错时候的处理,比如下面的代码中,当出错时判断错误类型是文件不存在,那就创建一个文件并且写入内容。
let file = "src/text.txt";let mut f = File::open(&file).unwrap_or_else(|err| {if err.kind() == ErrorKind::NotFound {println!("file is not found, create it");// create 创建的 fd 是 write only 的,不能 readlet mut wf = File::create(&file).unwrap();wf.write_all(b"hello world");wf} else {panic!("other error");}});
|err| {}这种写法是类似于 JS 中的回调函数,相当于(err) => {},举个例子( 泛型跟 TS 里的差不多 )
fn main() {let r2 = test_fn_once(|str| {let u: u32 = str.parse().expect("failed to parse to number");u});println!("{}", r2);}// FnOnce 的意思是这个函数只能调一次fn test_fn_once<T, F: Fn(String) -> T> (f: F) -> T{f(String::from("123"))}
更简易的写法
比 unwrap或 expect还有更简单的写法就是用 ? 操作符。两者的差异是,前者是遇到错误会 panic! ,后者则是返回 Result 不继续执行而不是 panic!,比如以下代码。
use std::error::Error;use std::fs::File;fn main() -> Result<(), Box<dyn Error>> {let f = File::open("hello.txt")?;Ok(())}
有点像 JS 中,try catch 后 return error 的感觉。正因为用了 ? 就会有返回值,所以就算是 main 函数,也要声明返回类型,Box<dyn Error> 代表任意错误。
通过 ? 的方式就可以链式调用了。
use std::error::Error;use std::fs::File;use std::io::Read;fn main() -> Result<(), Box<dyn Error>> {let mut s = String::new();File::open("hello.txt")?.read_to_string(&mut s)?;Ok(())}
