Post

Rust 异常

Rust 异常

使用宏 panic! 可以手动的中断程序,该操作造成的错误是不可以被恢复,程序中断后不会立刻释放和清除 Stack(堆)数据,等系统来回收。如果需要直接释放需要在 Cargo.toml 里加一个配置。

1
2
[profile.release]
panic = 'abort'

调用一个 panic。

1
2
3
fn main(){
  panic!("crash and run");
}
错误回溯

可以使用 RUST_BACKTRACE 配置进行错误回溯,可以追踪错误链路。

1
RUST_BACKTRACE=1 cargo run

Recoverable Error

在程序中很多时候错误是需要被捕获并恢复的,rust 中使用了 Result 来处理可恢复的错误。例如打开一个文件:

1
2
3
4
5
let f = File::open("hello.txt");
let f = match f {
  Ok(file) => file,
  Err(error) => panic!("Problem opening the file: {:?}", error),
}
匹配不同类型的错误

由于发生错误的原因可能有很多种,每种会返回不同类型的错误,就需要对类型错误进行匹配。

1
2
3
4
5
6
7
8
9
10
11
12
let f = match f {
  Ok(file) => file,
  Err(error) => match error.kind() {
    ErrorKind::NotFound => match File::create("hello.txt") {
      Ok(fc) => fc,
      Err(e) => panic!("Problem creating the file: {:?}", error)
    },
    other_error => {
      panic!("Problem opening the file: {:?}", error);
    }
  },
}
使用 unwrap_or_else 替代 match
1
2
3
4
5
6
7
8
9
let f = File::open("hello.txt").unwrap_or_else(|error| {
  if error.kind() == ErrorKind::NotFound {
    File::create("hello.txt").unwrap_or_else(|error| {
      panic!("Problem creating the file: {:?}", error)
    }) // 这里不能有;
  }else{
    panic!("Problem opening the file: {:?}", error);
  }
});
使用 unwrap 和 except 简化 Result

如果 Result 是 Err 时,unwrap 会自动调用 panic! 宏。

1
2
File::open("hello.txt").unwrap();
// called `Result::unwrap()` on an `Err` value: Error {repr: Os { code: 2, message: "No such file or directory" } }

如果是用 except 可以提供更好的错误信息,方便问题排查。

1
2
File::open("hello.txt").expect("Failed to open hello.txt");
// Failed to open hello.txt: Error { repr: Os { code:2, message: "No such file or directory" } }
错误传导

在实际编写中,很多错误处理后不需要中断程序,而是把错误封装后交给调用者处理。只需要将执行结果封装成 Result 并返回即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
fn read_username_from_file() -> Result<String, io::Error> {
  let f = File::open("hello.txt");
  let mut f = match f {
    Ok(file) => file,
    Err(e) => return Err(e),
  };

  let mut s = String::new();
  match f.read_to_string(&mut s) {
    Ok(_) => Ok(s),
    Err(e) => Err(e),
  }
}
使用 ? 处理错误

rust 提供 ? 操作符来简化了错误的处理。在表达式的尾部加上 ? 操作符表示获取错误后自动返回。

1
2
3
4
5
fn read_username_from_file() -> Result<String, io::Error> {
  let mut s = String::new();
  File::open("hello.txt")?.read_to_string(&mut s)?;
  Ok(s)
}

只有当函数返回的类型为 Result 和 Option 时才可以使用 ? 操作符。

使用 From Trait 进行错误自动转换

Rust 中提供了 From Trait,在进行类型匹配时,如果提供了从一个类型转换为另一个类型的方法(实现了某个类型的 From Trait),则在编译阶段,编译器会调用响应的函数,直接将其转为相应的类型!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#[derive(Debug)]
pub enum MyError {
    ReadError(String),
    ParseError(String),
}

impl From<std::io::Error> for MyError {
    fn from(source: std::io::Error) -> Self {
        MyError::ReadError(source.to_string())
    }
}

impl From<std::num::ParseIntError> for MyError {
    fn from(source: std::num::ParseIntError) -> Self {
        MyError::ParseError(source.to_string())
    }
}

fn read_file() -> Result<i64, MyError> {
    let _content = fs::read_to_string("/tmp/id")?;
    let content = _content.trim();
    let id = content.parse::<i64>()?;
    Ok(id)
}

fn main() -> Result<(), MyError> {
    let id = read_file()?;
    println!("id: {}", id);
    Ok(())
}

或者使用第三方库来进一步简化,例如thiserror

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#[derive(thiserror::Error, Debug)]
pub enum MyError {
    #[error("io error.")]
    IoError(#[from] std::io::Error),
    #[error("parse error.")]
    ParseError(#[from] std::num::ParseIntError),
}

fn read_file() -> Result<i64, MyError> {
    // Could get compiled!
    let content = fs::read_to_string("/tmp/id")?;
    let id = content.parse::<i64>()?;
    Ok(id)
}

fn main() -> Result<(), MyError> {
    let id = read_file()?;
    println!("id: {}", id);
    Ok(())
}

或者进一步使用 anyhow::Result 统一接收:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use anyhow::Result;
use std::fs;

fn read_file() -> Result<i64> {
    // Could get compiled!
    let content = fs::read_to_string("/tmp/id")?;
    let id = content.parse::<i64>()?;
    Ok(id)
}

fn main() -> Result<()> {
    let id = read_file()?;
    println!("id: {}", id);
    Ok(())
}
This post is licensed under CC BY 4.0 by the author.

Trending Tags