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枚举的,我们都可以使用该语法糖处理。

相关推荐
贾修行1 小时前
Kestrel:.NET 的高性能 Web 服务器探秘
服务器·前端·kestrel·.net·net core·web-server·asp.net-core
吃吃喝喝小朋友1 小时前
HTML DOM
前端·javascript·html
HWL56791 小时前
HTML中,<video> 和 <source> 标签
前端·javascript·html
球球不吃虾1 小时前
分享一个简单的交互式塔罗牌抽牌应用
前端·vue
2501_948120151 小时前
中职动漫设计与制作专业实训方案研究
前端·人工智能·语言模型·自然语言处理·架构
小小鸟0081 小时前
前端 RBAC基于角色的权限控制(按钮级别)
前端
学习java的小库里1 小时前
EasyExcel复杂导出
java·前端
muddjsv1 小时前
前端开发通用全流程:从需求到上线,步步拆解
前端
Mr Xu_2 小时前
从零实战!使用 Mars3D 快速构建水利监测 WebGIS 系统
前端·3d·webgis
wuhen_n2 小时前
类型断言:as vs <> vs ! 的使用边界与陷阱
前端·javascript·typescript