Day 06 - 错误处理机制

本贴最后更新于 600 天前,其中的信息可能已经水流花落

image

img

📚 不可恢复的错误与 panic!

创建时间:2023-04-01 18:36 星期六


Rust 错误处理的分类

Rust 的错误分为两类:

  • 不可恢复的错误

    • 索引 越界的情况 会出现 Bug
  • 可以恢复的错误

    • 文件读取的错误 我可以重新尝试
    • 用户因为超时链接失败的错误 我可以重新链接

通过返回 Result 是可恢复恐慌, 返回 panic!是不可回复恐慌

panic! 宏

不可恢复的错误 我们使用 panic! 宏来进行处理 ,它会使你的错误程序出现以下的执行;

你的程序会先进行错误消息的打印 然后 展开(unwind)​ /清理​/中止(abort)​ 调用栈,最后退出程序

当 恐慌: panic​ 发生的时候

  • **{展开调用栈}**​: Rust 会按照调用栈往回走清理每个遇到的函数的数据;

    • 工作量会很大
  • {中止} ​: [或者] 立即终止调用栈 不进行清理直接停止程序

    • 内存需要 OS 进行清理

如果你想压缩你的二进制文件大小 ,可以把展开设置为中止。我们可以在 Cargo.toml 中设置: (release 表示在生产环境中 )

[profile.release]
panic='abort'

然后我们的代码就可以这么写:

fn main() {
 panic!("错误的信息");
}

在执行一遍就会报错

thread 'main' panicked at '错误的信息', main.rs:2:2
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Result 枚举

如果有些报错我们需要进行捕捉我们可以使用枚举来查看某个操作是否没有异常,比如读取文件不存在就新增文件:

fn main() {
    let files = vec![
        "main.js".to_string(),
        "main.java".to_string(),
        "demo.c".to_string(),
    ];

    let ret = find_file(&files, "asdasd");
    match ret {
        Result::Err(_) => {
            println!("文件不存在 打开会失败!");
        },
        Result::Ok(filename) => {
            println!("文件存在 他是{}",filename);
        }
    }
}

fn find_file(files: &Vec<String>, filename: &str) -> Result<String, String> {
    for i in files.iter() {
        if filename == i {
            return Result::Ok(i.to_string());
        }
    }
    return Result::Err("No Such File".to_string());
}

这是 Result 枚举

enum Result<T,E>{
	Ok<T>, Err<E>
}

现在我们来试试真正的文件读取吧!

use std::{io::ErrorKind, fs::File};

fn main() {
    match File::open("main.cpp") {
        Result::Err(err) => match err.kind() {
            ErrorKind::NotFound => match File::create("main.cpp") {
                Ok(file) => {
                    println!("文件不存在 但是我给你创建了一个文件 不用谢谢我哈~!");
                    file
                },
                Err(e) => {
                    println!("创建main.cpp文件失败,{}",e);
                    panic!("错误")
                }
            },
            _other_error => {
                println!("文件发生未知错误");
                panic!("错误")
            }
        },
        Result::Ok(filename) => {
            println!("读取文件 成功!");
            filename
        }
    };
}

use std::fs::File;

use std::io::ErrorKind;

unwrap

Result 可以对 match 做一些简单处理,unwrap 就是其中一个。

比如我们的这个代码

use std::{
    fs::File,
    io::{ErrorKind, Read, Write},
};

fn main() {
    let f = File::open("main.cpp");
    match f {
        Ok(file) => file,
        Err(_) => {
            panic!("错误");
        }
    };
}

像是上面的代码我们可以

File::open("main.cpp").unwrap();

如果你想指定信息 你可以使用 except

File::open("main.cpp").expect("这是很离谱的错误 你不该犯这样的错误的!");

except 还可以定位错误的信息源哦 unwrap 是做不到的。

传播错误

我们先看看以下的代码:

use std::{
    fs::File,
    io::{Error, Read, Write},
};
/**
 * 尝试查看save.txt存在否 如果不存在就创建一个
 */
fn create() ->  File {
    //region
    let mut f = match File::open("save.txt") {
        Result::Ok(file) => file,
        Result::Err(err) => {
            println!("没有指定的文件现在给你重新写一个");
            match File::create("save.txt") {
                Ok(file) => file,
                Err(err) => {
                    panic!("文件无法被建立");
                }
            }
        }
    };
    f
}

fn read_file(mut f: File) -> Result<String, Error> {
    // region 尝试读取
    let mut ret: String = String::new();
    match f.read_to_string(&mut ret) {
        Ok(_) => Ok(ret),
        Err(e) => Err(e)
    }
    // endregion
}

fn main() {
    let mut f = create();
    let ret = read_file(f);
}

在 read_file 中我们讲阅读的最终结果,要么 Ok 要么失败,这里我们将错误返回了出来,我们依旧可以简化他。

?

这里我们需要学习一个语法:

let mut f = File::read("filename") ?;

? 表示如果 File::read 返回的 Result 是 Ok 那么我会将 Ok 里面的值返回出来 比如

Result::Ok(file) => file

如果是 Err 那么我会将错误返回出来 如

Result::Err(e)=> return Err(e)

所以这段代码类似于:

let mut f = match File::open("filename") {
    Result::Ok(file) => file,
    Result::Err(e)=> return Err(e)
};

All in all 我们可以简写代码为:

fn read_file() -> Result<String, Error> {
    let mut f = File::open("save.txt") ?;

    let mut ret: String = String::new();
    f.read_to_string(&mut ret) ?;
    Ok(ret) // 将文件读取的内容利用ret进行封装
}

from 函数

from 用于错误之间的转换,被? 所应用的错误会隐式的被 from 进行处理, 当?调用 from 的时候:

  • 他所接受的错误类型会被转化为当前函数返回类型所定义的类型

    • 错误类型 A 转换错误类型 B 的条件: 错误类型 A 实现了一个 from 函数他的返回类型时错误类型 B

在 Rust 中,使用 ?​ 运算符可以将一个可能会返回 Result​ 类型的函数的返回值进行自动解包。如果函数返回的是 Ok​,那么 ?​ 运算符会将 Ok​ 中的值返回;如果函数返回的是 Err​,那么 ?​ 运算符会将 Err​ 直接返回,同时也会将函数的执行流程立即返回到调用该函数的地方。

换句话说,如果在某个函数中使用了 ?​ 运算符,当函数返回的是 Err​ 时,该函数的执行流程会立即返回到调用该函数的地方,不会继续执行函数中 ?​ 运算符后面的代码。 也就是说 ?​具有返回数据的效果~

如何合法的在 main 函数使用 ?​ ?

use std::error::Error as error;

fn main() -> Result<(), Box<dyn error>> {
    let mut f = create(); 
    let mut f = File::open("save.txt") ?;
    Ok(())
}
// Box <dyn Error> 表示任何可能出现的错误

什么时候使用 panic!

当你觉得某些时候可能会发生错误的时候你可以使用 result,否则你就使用 panic 制造恐慌。

image

  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...