Rust学习_3.错误处理

Rust 错误处理

一、错误的分类

  • 可恢复(例如文件未找到);
    • Result<T,E>
  • 不可恢复(bug,例如访问索引超限);
    • panic!

二、不可恢复的错误与panic!

1. 当 panic!宏执行:

  • 你的程序会打印一个错误信息;
  • 展开(unwind)、清理调用栈(Stack);
  • 退出程序

2. 为应对panic,展开或中止调用栈

  • 默认情况下,当panic发生:
    • 程序展开调用栈(工作量大)
      • Rust沿着调用栈往回走;
      • 清理每个遇到的函数中的数据;
    • 或终止调用栈
      • 不进行清理,直接停止程序;
      • 内存需要OS进行清理
  • 想让二进制文件更小,把设置从“展开”改为“中止“:
    • Cargo.toml 中适当的profile部分设置:
      • panic=’abort’

三、Result 枚举与可恢复错误

1. Result 枚举

1
2
3
4
enum Result<T,E>{
Ok(T),
Err(E),
}
  • T:操作成功情况下,Ok变体里返回的数据的类型;
  • E:操作失败情况下,Err变体里返回的错误的类型;

2. 处理Result的一种方式,match表达式

3. unwrap

  • match 表达式的一个快捷方式
  • 如果Result结果是Ok,返回Ok里面的值;
  • 如果Result结果是Err,调用panic!宏;
  • 缺点:错误信息不能自定义!
1
2
3
4
5
6
7
8
9
10
11
12
use std::fs::File;
fn main(){
let f = File::open("hello.txt");
let f = match f{
Ok(file)=>file,
Err(error)=>{
panic!("error opening file {}", error);
}
}

let f = File::open("hello.txt").unwrap(); // 等价于上面的代码
}

4. expect 可以自定义的unwrap

四、传播错误

1. 将错误返回给调用者

1
2
3
4
5
6
7
8
9
10
11
12
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)=> return Err(e),
}
}

2. ?运算符

?运算符:传播错误的一种快捷方式;

  • 如果Result 是 Ok: Ok中的值就是表达式的结果,然后继续执行程序;
  • 如果Result 是Err:Err就是整个函数的返回值,就像使用了return;
1
2
3
4
5
6
7
// 该函式等同于上面的函数
fn read_username_from_file()->Result<String,io::Error>{
let mut f = File::open("hello.txt")?; // ?运算符
let mut s = String::new();
f.read_to_string(&mut s)?; // ?运算符
Ok(s)
}

3. ?from函数

  • Trait std::convert::From 上的from函数;

    • 用于错误之间的转换
  • ?所应用的错误,会隐式的被from函数处理;

  • ?调用from函数时:

    • 它所接收的错误类型会转化为当前函数返回类型所定义的错误类型;
  • 用于: 针对不同错误原因,返回同一种错误类型

    • 只要每个错误类型实现了转换为所返回的错误类型的from函数;
  • 链式调用:

    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类型的函数

4. ?运算符与main函数

  • main 函数返回类型是();

  • main函数的返回类型也可以是:Result<T,E>;

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

五、 什么时候使用panic!

1. 总体原则

  • 在定义一个可能失败的函数时,优先考虑返回Result;
  • 否则就panic!

2. 编写示例、原型代码、测试

  • 可以使用panic!
    • 演示某些概念:unwrap;
    • 原型代码:unwrap、expect;
    • 测试:unwrap、expect;