Panic

类似于 JS 中的 throw new Error ,在 Rust 中是调用 panic! 这个宏抛出异常

  1. fn main() {
  2. panic!("error");
  3. }

如果加上 RUST_BACKTRACE的环境变量执行 Rust 程序,当抛出异常时会把详细的堆栈信息打印出来

  1. $ RUST_BACKTRACE=1 cargo run

Result

类似于 Option<T> ,针对错误信息,Rust 提供了一个叫 Result 的枚举。

  1. enum Result<T, E> {
  2. Ok(T),
  3. Err(E),
  4. }

比如文件操作,返回的就是 Result 这个枚举,可以通过 match 获取到正确的结果。

  1. use std::fs::File;
  2. fn main() {
  3. let f = File::open("src/text.txt");
  4. let mut f = match f {
  5. Ok(file) => file,
  6. Err(e) => panic!("read file filed, {:?}", e),
  7. };
  8. let mut str = String::new();
  9. let r = f.read_to_string(&mut str);
  10. match r {
  11. Ok(r) => {
  12. println!("content is {}, size is {}", str, r);
  13. },
  14. Err(e) => panic!("read file filed, {:?}", e),
  15. }
  16. }

unwrap & expect

按照枚举的用法,如果总是要 match 的话太繁琐,所以 Rust 给 Result枚举提供了一些好用的方法,可以通过 unwrap 或者 expect来简写逻辑。

  1. use std::fs::File;
  2. fn main() {
  3. // 其实 fs 提供了更简单的方法,可以直接 fs::read_to_string("src/text.txt")。
  4. // 这里 demo 先不这么写了。
  5. let mut f = File::open("src/text.txt").unwrap();
  6. let mut str = String::new();
  7. let r = f.read_to_string(&mut str).expect("read file filed");
  8. println!("content is {}, size is {}", str, r);
  9. }

unwrap 或者 expect都相当于帮做了 match 的逻辑,如果成功返回结果,失败则抛出异常,两个函数的不同之处就是 unwrap 不接收自定义的错误信息,但是 expect会接收。

自己写的函数也可以返回 Result 这个枚举。跟 Option<T> 一样,Result 的枚举可以直接使用( Ok 和 Err )

  1. use rand::{thread_rng, Rng};
  2. fn main() {
  3. let f = test_result().unwrap();
  4. println!("{}", f);
  5. }
  6. fn test_result() -> Result<String, String> {
  7. let rand: u8 = thread_rng().gen_range(0..10);
  8. if rand > 5 {
  9. Ok("123")
  10. } else {
  11. Err("123")
  12. }
  13. }

除了使用 unwrap,还可以用 unwrap_or_else 来对错误进行更进一步的处理,unwrap_or_else支持传入一个回调函数,也就是出错时候的处理,比如下面的代码中,当出错时判断错误类型是文件不存在,那就创建一个文件并且写入内容。

  1. let file = "src/text.txt";
  2. let mut f = File::open(&file).unwrap_or_else(|err| {
  3. if err.kind() == ErrorKind::NotFound {
  4. println!("file is not found, create it");
  5. // create 创建的 fd 是 write only 的,不能 read
  6. let mut wf = File::create(&file).unwrap();
  7. wf.write_all(b"hello world");
  8. wf
  9. } else {
  10. panic!("other error");
  11. }
  12. });

|err| {} 这种写法是类似于 JS 中的回调函数,相当于 (err) => {} ,举个例子( 泛型跟 TS 里的差不多 )

  1. fn main() {
  2. let r2 = test_fn_once(|str| {
  3. let u: u32 = str.parse().expect("failed to parse to number");
  4. u
  5. });
  6. println!("{}", r2);
  7. }
  8. // FnOnce 的意思是这个函数只能调一次
  9. fn test_fn_once<T, F: Fn(String) -> T> (f: F) -> T{
  10. f(String::from("123"))
  11. }

更简易的写法

unwrapexpect还有更简单的写法就是用 ? 操作符。两者的差异是,前者是遇到错误会 panic! ,后者则是返回 Result 不继续执行而不是 panic!,比如以下代码。

  1. use std::error::Error;
  2. use std::fs::File;
  3. fn main() -> Result<(), Box<dyn Error>> {
  4. let f = File::open("hello.txt")?;
  5. Ok(())
  6. }

有点像 JS 中,try catch 后 return error 的感觉。正因为用了 ? 就会有返回值,所以就算是 main 函数,也要声明返回类型,Box<dyn Error> 代表任意错误。

通过 ? 的方式就可以链式调用了。

  1. use std::error::Error;
  2. use std::fs::File;
  3. use std::io::Read;
  4. fn main() -> Result<(), Box<dyn Error>> {
  5. let mut s = String::new();
  6. File::open("hello.txt")?.read_to_string(&mut s)?;
  7. Ok(())
  8. }