Rust第九节 - 错误处理

9 错误处理

在Rust中,我们的错误处理有两种,分为可恢复处理和不可恢复处理。

9.1 不可恢复错误处理

9.1.1 panic

我们首先举一个例子,这个例子会引发panic的报错,例如:

rust 复制代码
let arr = vec![1, 2, 3];
println!("{}", arr[100]);

当我们出现越界访问的时候,这个时候会触发panic报错,导致程序崩溃,我们在开发的时候应该尽量注意。这是个不可恢复的处理。

9.1.2 错误回溯

当我们需要查看错误回溯的时候,我们可以使用RUST_BACKTRACE=1 cargo run命令,例如我们手动触发一个panic!报错:

rust 复制代码
fn main(){ // 在根文件中使用 panic! 抛出错误
    panic!("Error...")
}

// 控制台执行命令  RUST_BACKTRACE=1 cargo run

9.2 可恢复错误处理

我们在日常的开发中,更多的情况是,针对不同的错误进行不同的处理。

9.2.1 Result 枚举

Rust在预模块中替我们引入Result枚举,里面包含了OKErr两个变体的,OK表示正确变体,Err表示错误变体。接下来让我们来手动处理错误。

9.2.2 手动处理错误

我们尝试写一段代码来打开一个不存在的文件,例如:

rust 复制代码
use std::fs::read;
let f = read("./hello-world.txt");
println!("{:?}", f)

因为我们这个文件是不存在的,所以这个时候会返回一个Err变体,这个时候我们可以使用match去处理,当我们的文件不同时,我们就创建这个文件,例如:

rust 复制代码
use std::fs;
use std::io::ErrorKind;

let f = fs::File::open("./hello-world.txt");
match f {
    Ok(data) => {
        println!("{:?}", data);
    }
    Err(error) => {
        return match error.kind() {
            ErrorKind::NotFound => {
                let create_data = fs::File::create("./hello-world.txt");
                match create_data {
                    Ok(file_data) => {
                        println!("{:?}", file_data);
                    }
                    Err(err) => {
                        println!("{}", err);
                    }
                }
            }
            _ => {
                println!("{}", error);
            }
        };
    }
}

上面我们手动去处理读取文件失败的错误,然后创建文件,但是我们也要对创建文件去手动做错误处理,显得太麻烦了,于是我们可以用以下的方法去简化

9.2.3 unwrap和expect快速处理错误

对于有返回Result枚举的方法、函数等,我们都可以使用unwrapexpect去处理。 对于unwrap,就相当于我们使用match去处理错误,只不过它会返回一个Rust默认的错误,例如:

rust 复制代码
use std::fs;

fs::File::open("./hello-world.txt").unwrap()

expect,我们则可以穿入一个字符串,提示我们想提示的内容,例如:

rust 复制代码
fs::File::open("./hello-world.txt").expect("创建文件失败");

9.2.4 向上返回结果和错误体

对于刚刚嵌套的问题,我们可以将其拆分成为多个函数,然后把我们读取或者创建文件后的Result变体想外部抛出,然后上一层去获取并且处理。我们现封装一个读取文件的函数,例如:

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

// 读取文件
fn read_file(path: &str) -> Result<String, io::Error> {
    let content = match fs::File::open(path) {
        Ok(data) => data,
        Err(error) => {
            println!("{:?}", error);
            return Err(error);
        }
    };
    return Ok(content);
}

// 创建文件
fn create_file(path: &str) {
    match fs::File::create(path) {
        Ok(data) => {
            println!("{:?}", data);
        }
        Err(error) => {
            println!("{:?}", error)
        }
    }
}

// 在最外层处理封装函数的逻辑
let content = match read_file("./hello-world.txt") {
    Ok(data) => data,
    Err(error) => {
        return match error.kind() {
            ErrorKind::NotFound => {
                create_file("./hello-world.txt");
            }
            _ => {
                println!("其他错误");
            }
        };
    }
};

9.2.5 优化处理错误处理(?)

即使我们做了函数的封装,让嵌套变少了,但是还是显得很麻烦,那还没有更简单的方法呢?当然有,我们有语法糖?,它会将存储在Ok内部的值返回给外部的变量。如果出现了错误,?就会提前结束整个函数的执行,并将任何可能的Err值返回给函数调用者,例如:

rust 复制代码
fn read_file() -> Result<String, io::Error> {
    let mut str: String = String::new();
    fs::File::open("./hello-world.txt")?.read_to_string(&mut str)?;
    println!("{}", str);
    Ok(str)
}

使用该方法,如果文件不存在,会自动把错误抛出,而不会panic崩溃。 所有返回Result或者Option枚举的,我们都可以使用该语法糖处理。

相关推荐
我是苏苏19 分钟前
C# Main函数中调用异步方法
前端·javascript·c#
转角羊儿30 分钟前
uni-app文章列表制作⑧
前端·javascript·uni-app
大G哥36 分钟前
python 数据类型----可变数据类型
linux·服务器·开发语言·前端·python
hong_zc1 小时前
初始 html
前端·html
小小吱1 小时前
HTML动画
前端·html
糊涂涂是个小盆友1 小时前
前端 - 使用uniapp+vue搭建前端项目(app端)
前端·vue.js·uni-app
浮华似水2 小时前
Javascirpt时区——脱坑指南
前端
王二端茶倒水2 小时前
大龄程序员兼职跑外卖第五周之亲身感悟
前端·后端·程序员
_oP_i2 小时前
Web 与 Unity 之间的交互
前端·unity·交互
钢铁小狗侠2 小时前
前端(1)——快速入门HTML
前端·html