Rust学习记录--C9 错误处理

C9 错误处理

  • [9.1 panic! 不可恢复的错误](#9.1 panic! 不可恢复的错误)
  • [9.2 Result枚举类型](#9.2 Result枚举类型)
    • [9.2.1 定义](#9.2.1 定义)
    • [9.2.2 如何处理Result的结果:match表达式](#9.2.2 如何处理Result的结果:match表达式)
    • [9.2.3 unwrap 取代match](#9.2.3 unwrap 取代match)
    • [9.2.4 except:可以指定信息的unwrap](#9.2.4 except:可以指定信息的unwrap)
  • [9.3 传播错误](#9.3 传播错误)
    • [9.3.1传播错误: 将错误传给调用者](#9.3.1传播错误: 将错误传给调用者)
    • [9.3.2 ?运算符](#9.3.2 ?运算符)
    • [9.3.3 ?与from函数](#9.3.3 ?与from函数)
    • [9.3.4 ?只能用于返回Result函数](#9.3.4 ?只能用于返回Result函数)
  • [9.4 什么时候应该使用panic!](#9.4 什么时候应该使用panic!)
    • [9.4.1 总体原则](#9.4.1 总体原则)

9.1 panic! 不可恢复的错误

  • 错误的分类
    • 可恢复:例如文件未找到,可以再次changs
    • 不可恢复:代码有bug,例如索引超出范围
  • Rust没有类似的异常机制
    • 可恢复错误:Result<T,E>
    • 不可恢复:panic! 宏
  • 当panic发生时
    • 程序展开调用栈(工作量大)
      • rust沿着调用栈往回走
      • 清理每个遇到的函数中的数据
    • 立即中止调用栈
      • 不进行清理,直接停止程序
      • 内存需要os进行清理
    • 想让二进制文件更小,将"展开"改为"中止"
      • 在Cargo.toml中的profile部分设置
    • RUST_BACKTRACE=1
    • RUST_BACKTRACE=full

9.2 Result枚举类型

一般而言,错误的发生不一定要中止程序运行,例如:文件不存在,常见操作是创建这个文件,而不是直接panic

=> Result类型处理可能失败的情况

9.2.1 定义

rust 复制代码
// T和E是泛型类型参数
// Ok和Err关联不同的数据,Ok关联T类型的数据,Err关联E类型的数据
enum Result<T,E> {
	Ok(T),
	Err(E),
}

9.2.2 如何处理Result的结果:match表达式

  • 与Option类似
  • 例子
rust 复制代码
use std::fs::File;

fn main()
{
    let f = File::open("hello.txt");

    let f_file = match f {
        Ok(file) => file,
        Err(error) =>{
            panic!("Error opening file {:?}", error);
        }
    };
}
  • 匹配不同的错误
rust 复制代码
error.Kind()
// 返回值ErrorKind 枚举,由标准库提供,用于描述io操作可能会一起的不同的错误
rust 复制代码
use std::{fs::File, io::ErrorKind};

fn main()
{
    let f = File::open("Hello.txt");

    let f_file = match f {
        Ok(file) => file,
        Err(error) =>{
        		// 根据error.kind()不同的类型做出不同的判断
            match error.kind() {
            		// 当是NotFound的时候创建这个file
            		// 于此同时创建file也可能会发生error
                ErrorKind::NotFound => match File::create("Hello.txt") {
                    Ok(file) => file,
                    Err(e) => panic!("Error for creating file {:?}", e),
                }
                // 不关心其他的error类型,统一用other_error 代替,名字可以替换
                other_error => panic!("Error for opening file {:?}", other_error),
            }
        }
    };
}

运行后创建Hello.txt

  • match表达式有点原始
  • 闭包(closure)。Result<T,E>有很多方法
    • 接收闭包作为参数
    • 使用match实现
    • 代码简洁
rust 复制代码
use std::{fs::File, io::ErrorKind};

fn main()
{
    let f = File::open("Hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("Hello.txt").unwrap_or_else(|error| {
                panic!("Error for creating file {:?}", error);
            })
        }
        else {
            panic!("Error for opening file:{:?}", error)
        }
    });
}

9.2.3 unwrap 取代match

  • unwrap:match表达式的快捷方法
    • 如果Result是Ok,则返回Ok里面的值
    • 如果Result是Err,则调用panic!
  • 缺点:错误信息不可以自定义

对比

rust 复制代码
use std::fs::File;

fn main()
{
    let f = File::open("hello.txt");

    let f_file = match f {
        Ok(file) => file,
        Err(error) =>{
            panic!("Error opening file {:?}", error);
        }
    };
}
rust 复制代码
use std::fs::File;

fn main()
{
    let f = File::open("hello.txt");

    let f_file = f.unwrap();
}

输出:

rust 复制代码
thread 'main' (25764) panicked at src\main.rs:7:20:
called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\hello_cargo.exe` (exit code: 101)

9.2.4 except:可以指定信息的unwrap

  • except:与unwrap类似,但是可以指定错误信息
  • 例子
rust 复制代码
use std::fs::File;

fn main()
{
    let f = File::open("hello.txt");

    let f_file = f.expect("Error for opening file");
}

输出:

rust 复制代码
thread 'main' (12996) panicked at src\main.rs:7:20:
Error for opening file: Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\hello_cargo.exe` (exit code: 101)

9.3 传播错误

9.3.1传播错误: 将错误传给调用者

rust 复制代码
use std::{fs::File, io, io::Read};

fn main()
{
    let res = read_username_from_file();
}

fn read_username_from_file() -> Result<String, io::Error>{
    // open the file and handle error
    let f = File::open("Hello.txt");
    let mut f = match f {
        Ok(ff) => ff,
        Err(e) => return Err(e),
    };

    // read string from file
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

9.3.2 ?运算符

  • 传播错误的快捷方式
  • 返回值:
    • 如果Result是Ok:Ok中的值就是表达式的结果,然后继续执行程序
    • 如果Result是Err:Err就是整个函数的返回值,就相当于使用return
      对比:
rust 复制代码
let f = File::open("Hello.txt");
let mut f = match f {
    Ok(ff) => ff,
    Err(e) => return Err(e),
};

将变量加上mut,在最后加上?

rust 复制代码
let mut f = File::open("Hello.txt")?;
};

举例:

rust 复制代码
use std::{fs::File, io, io::Read};

fn main()
{
    let res = read_username_from_file();
}

fn read_username_from_file() -> Result<String, io::Error>{
    // open the file and handle error
    let mut f = File::open("Hello.txt")?;

    // read string from file
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    
    return Ok(s);
}

9.3.3 ?与from函数

  • Trait std::convert::From上的from函数
    • 用于错误转换:将一个错误类型转换为另一个错误类型
  • 被?所处理的错误,会隐式的被from函数处理
  • 应用:针对不太错误原因,返回同一种错误类型
    • 要求:每个错误类型实现了转换为所返回的错误类型的from函数
      再次修改,链式调用
rust 复制代码
use std::{fs::File, io, io::Read};

fn main()
{
    let res = read_username_from_file();
}

fn read_username_from_file() -> Result<String, io::Error>{
    let mut s = String::new();
    File::open("Hello.txt")?.read_to_string(&mut s)?;    
    return Ok(s);
}

9.3.4 ?只能用于返回Result函数

  • main函数的返回类型是()
  • 如果直接在main函数中使用()会报错
rust 复制代码
use std::{fs::File};

fn main()
{
    let mut f = File::open("Hello.txt")?
}

根据help的提示,可以修改代码:

rust 复制代码
use std::{fs::File};

fn main() ->  Result<(), Box<dyn std::error::Error>>
{
    let mut f = File::open("Hello.txt")?;
    Ok(())
}
  • Box<dyn std::error::Error>是trait对象,可以简单理解为"任何困难的错误类型"

9.4 什么时候应该使用panic!

9.4.1 总体原则

  • 原则
    • 在定义一个可能失败的函数时,优先考虑返回Result
    • 否则就panic!
  • 场景建议
    • 调用你的代码,传入无意义的参数值:panic!
    • 调用外部不可控代码,返回非法状态,你无法修复:panic!
    • 如果失败是可预期的:Result
    • 当你的代码对值进行操作,首先应该验证这些值,当这些值不合法时:panic!
      TODO但是我感觉我在代码中可能用不到?
      例子
rust 复制代码
if guess < || guess > 100 {
	println!("The secret number should be between 1 and 100");
	continue;
}

如果很多函数都需要这个验证=>创建一个新的类型,将验证逻辑放在构造事例的函数里

只有通过验证的才能创建相应的实例,否则不能创建实例

rust 复制代码
pub struct Guess{
    value: i32,
}

impl Guess{
    // 相当于构造函数
    pub fn new(value: i32) -> Guess {
    		// 验证逻辑
        if value < 1 || value > 100 {
            panic!("The secret number should be between 1 and 100");
        }
        // 创建实例
        Guess { value}
    }

		// 类似C#的getter,用于读取字段值,因为在struct中value是私有字段
    pub fn value(&self) -> i32 {
        self.value
    }
}


fn main()
{
	let num = 12;
	let guess = Guess::new(num);
	
	// num2 不合法,会触发panic
	let num2 = -1;
	let guess2 = Guess::new(num2);
}
  • getter:返回字段数据
    • 字段是私有的:外部无法直接对字段赋值
rust 复制代码
impl Guess{
	
	pub fn value(&self) -> i32 {
	        self.value
	    }
}
相关推荐
局外人LZ2 小时前
libsodium.js:web端与 Node.js 的现代加密工具集,构建前端安全加密体系
前端·javascript·node.js
xkxnq2 小时前
第二阶段:Vue 组件化开发(第 20天)
前端·javascript·vue.js
「、皓子~2 小时前
AI 创作系列(34)海狸IM桌面版 v1.1 正式发布:Vite + Electron 性能优化与体验升级
前端·人工智能·electron·开源·开源软件·im
鹏程十八少2 小时前
1.Android 3分钟跑通腾讯 Shadow 插件化官方Demo:零反射、手把手实战(基于源码依赖)
android·前端·面试
光影少年2 小时前
electron通信方式有哪些?
前端·javascript·electron
CodeSheep2 小时前
这个老牌知名编程论坛,彻底倒下了!
前端·后端·程序员
hakuii2 小时前
3dgs学习有感
学习·3d
李泽辉_2 小时前
深度学习算法学习(六):深度学习-处理文本:神经网络处理文本、Embedding层
深度学习·学习·算法
BD_Marathon2 小时前
搭建MyBatis框架之创建mapper接口(四)
java·前端