入门Rust的固定套路:错误处理模式有三大类,帮你总结了
2023-12-20 17:47:22 软件 268观看
摘要最近在学习总结Rust的各种场景的语法模式,也就是Rust写代码的模式。今天分享关于Rust的错误处理的三大类语法模式。先列出一个大纲第一类:有意不处理错误,忽略错误unwrap().fn( )? 符号,代替rust早期版本中的try!宏第二类

最近在学习总结Rust的各种场景的语法模式,也就是Rust写代码的模式。zEN28资讯网——每日最新资讯28at.com

今天分享关于Rust的错误处理的三大类语法模式。zEN28资讯网——每日最新资讯28at.com

zEN28资讯网——每日最新资讯28at.com

先列出一个大纲

第一类:有意不处理错误,忽略错误zEN28资讯网——每日最新资讯28at.com

  • unwrap()
  • .fn( )? 符号,代替rust早期版本中的try!宏

第二类:对错误做自定义信息提示zEN28资讯网——每日最新资讯28at.com

  • 使用expect()。

第三类:推荐!根据正确和错误情况分开处理,错误还可以进一步分流处理zEN28资讯网——每日最新资讯28at.com

  • match(包括 match处理 Result<T,E>或  match处理Option<>, 或 使用map_err())
  • 使用if let Some(value)= fn() {} else {}
  • 使用特定的函数:and_then() 和 or_else()

我对Rust的错误处理的印象

Rust的错误处理方式比起Golang更灵活,可以针对错误和当下代码需要赋值前做错误判断、故意忽略异常、异常时打印错误并终止等的不同编码场景,选用不同的语法模式。zEN28资讯网——每日最新资讯28at.com

错误处理涉及到数据类型、错误处理的控制语法、相关的crate模块。zEN28资讯网——每日最新资讯28at.com

Rust错误处理涉及到的数据类型

错误处理的类型1:Result

Result属于Rust的核心crate提供的功能(core::result::Result)。Result 有2个特点:zEN28资讯网——每日最新资讯28at.com

首先,Result<T,E> 属于泛型,所以T和E可以是任意类型。但常见的使用模式是以T存储正常情况的信息,E存储错误和异常情况的信息。zEN28资讯网——每日最新资讯28at.com

其次,Result是枚举类型,其内部实际只包含:zEN28资讯网——每日最新资讯28at.com

Ok()Err()

这两个枚举,所以它的实例只会是Ok()或Err()之一。zEN28资讯网——每日最新资讯28at.com

Result的代码实现中就用的枚举类型的,代码如下:zEN28资讯网——每日最新资讯28at.com

/// Result的定义在 rust核心代码 src/rust/library/core/src/result.rs 代码文件中:/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).////// See the [module documentation](self "module documentation") for details.#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]#[must_use = "this `Result` may be an `Err` variant, which should be handled"]#[rustc_diagnostic_item = "Result"]#[stable(feature = "rust1", since = "1.0.0")]pub enum Result<T, E> {    /// Contains the success value    #[lang = "Ok"]    #[stable(feature = "rust1", since = "1.0.0")]    Ok(#[stable(feature = "rust1", since = "1.0.0")] T),    /// Contains the error value    #[lang = "Err"]    #[stable(feature = "rust1", since = "1.0.0")]    Err(#[stable(feature = "rust1", since = "1.0.0")] E),}

实际使用的一个例子:例如Result<String, &str> 是一个泛型类型,则此时的T类型为String,E类型为&str。它表示一个可能成功或失败的操作的结果,其中成功时的返回类型为 String,失败时的错误类型为 &str。zEN28资讯网——每日最新资讯28at.com

总结一下Result<Ok(),Err()>:  result 类型通常用于表示可能成功或失败的操作的结果。它使用 Result<T, E> 类型来表示,其中 T 是成功时的返回类型,E 是失败时的错误类型。zEN28资讯网——每日最新资讯28at.com

错误处理的类型2:Option

Option也是枚举类型,其内部实际只包含两种值:Some()和None,这个可以从其代码中得到印证。Option的实现代码为:zEN28资讯网——每日最新资讯28at.com

/// 代码文件 rustlib/src/rust/library/core/src/option.rs 定义了Option/// The `Option` type. See [the module level documentation](self "the module level documentation") for more.#[derive(Copy, PartialOrd, Eq, Ord, Debug, Hash)]#[rustc_diagnostic_item = "Option"]#[lang = "Option"]#[stable(feature = "rust1", since = "1.0.0")]pub enum Option<T> {    /// No value.    #[lang = "None"]    #[stable(feature = "rust1", since = "1.0.0")]    None,    /// Some value of type `T`.    #[lang = "Some"]    #[stable(feature = "rust1", since = "1.0.0")]    Some(#[stable(feature = "rust1", since = "1.0.0")] T),}

Result 和 Option 有什么区别?

首先,从名称上理解一下:zEN28资讯网——每日最新资讯28at.com

  • Result 中文意思是结果。一般表示成功或失败,所以它的枚举值会是Ok()和Err();
  • Option,中文意思是选项。那么它的侧重点不是成功失败,而是不同的选项(有选项则为Some()、无选项则为None),所以它的枚举值也很清楚,只有Some()和None两个值。

其次,它们之间可以互相转化。对于错误处理来说,match处理Result或Option时,都要处理错误或None等异常值,就实现了Rust的最重要的错误处理逻辑。zEN28资讯网——每日最新资讯28at.com

错误处理的控制语法的具体分析和举例

1.Rust的第一类错误处理模式:忽略错误,不处理错误:

(1) unwrap()zEN28资讯网——每日最新资讯28at.com

在 Rust 中,unwrap() 方法用于从 Result 类型中提取成功时的返回值。如果 Result 类型的值是 Ok(表示成功),则 unwrap() 方法将返回 T;如果 Result 类型的值是 Err(表示失败),则 unwrap() 方法将触发一个 panic,抛出一个 E 类型的错误。如果您在调用 unwrap() 方法时遇到错误,说明您正在处理一个 Err 类型的值,即失败的情况。在这种情况下,您应该使用其他方法来处理错误,而不是直接使用 unwrap() 方法。总之,在处理 Result 类型时,应该始终考虑可能的失败情况,并使用适当的方法来处理错误。直接使用 unwrap() 方法可能会导致程序崩溃(panic),因此应该谨慎使用。只应该在非生产的代码中使用。zEN28资讯网——每日最新资讯28at.com

(2) .fn()?符号zEN28资讯网——每日最新资讯28at.com

这个符号在Rust中的术语是“提前返回选项”(early return option),作用等同于unwrap()。只允许用于返回Result<>或Option<>类型的函数之后。在rust的早期版本中,有个try!宏具有等效的功能。zEN28资讯网——每日最新资讯28at.com

代码演示:(演示打开文件,返回Result<File, Error> 类型的值,然后可以被main()中的match方式处理):zEN28资讯网——每日最新资讯28at.com

use std::fs::File;use std::io::Error;fn open_file(file_path: &str) -> Result<File, Error> {    let mut file = File::open(file_path)?;    Ok(file)    }

这段函数内部使用File::open(file_path)?; 打开指定路径的文件,open()是rust的内部函数,原始定义为:zEN28资讯网——每日最新资讯28at.com

pub fn open<P>(path: P) -> io::Result<File>

而演示代码中有意忽略了错误的情况,以?结尾,来调用open()函数:zEN28资讯网——每日最新资讯28at.com

let mut file = File::open(file_path)?;

2.Rust的第二类错误处理模式:对错误做自定义信息提示:

expect() 可作为代替unwrap()或 ? 相比unwrap(), expect()是一个更好的选择,因为它允许发生错误时打印一个简单的消息并终止运行。zEN28资讯网——每日最新资讯28at.com

3.Rust的第三类错误处理模式(推荐!):

根据正确和错误情况分开处理,错误还可以进一步分流处理。zEN28资讯网——每日最新资讯28at.com

(1) match,根据正确和错误情况分开处理。zEN28资讯网——每日最新资讯28at.com

  • 使用match 分流处理 Result<T,E>中包含错误的情况处理:
use std::fs::File;use std::io::Error;/// 演示打开文件,返回Result<File, Error> 类型的值,然后被main()中的match方式处理fn open_file(file_path: &str) -> Result<File, Error> {    let mut file = File::open(file_path)?;    Ok(file)}fn main() {    let file_path = "file.txt";    let file = open_file(file_path);    match file {        Ok(file) => println!("文件打开成功 {:?}", file),        Err(error) => println!("文件打开失败 {}", error),    }}
  • 使用 match 分流处理 Option<>包含错误的情况处理:
use std::fs::File;use std::io::ErrorKind;use std::io::{Error, Read};/// 演示文件打开时 如何返回Option<T> 类型值fn open_file(file_path: &str) -> Option<File> {    let mut file = File::open(file_path).unwrap();    Some(file)}/// 演示 match 如何处理 Option<T> 类型值,其中有None类型的情况fn read_file(file: Option<File>) -> Result<String, Error> {    match file {        //处理文件的正常情况        Some(mut file) => {            let mut buffer = String::new();            let file_content = file.read_to_string(&mut buffer);            Ok(buffer)        }        //处理文件的异常情况        None => Err(Error::new(ErrorKind::NotFound, "File not found")),    }}fn main() {    let file_path = "file.txt";    let file = open_file(&file_path);    let strings_in_file = read_file(file);    /// 在文件 file.txt 不存在的情况下,以下代码会导致软件崩溃。    println!("{}", strings_in_file.unwrap());    ///echo "1111">> file.txt    /// 创建 file.txt文件    /// 然后重复上面代码}
  • 使用 map_err() 链式处理

map_err()  将在以后发布的文章中再讲解。本文不做详细介绍。zEN28资讯网——每日最新资讯28at.com

(2) if let,适合直接在赋值前做错误处理。代码模式为:zEN28资讯网——每日最新资讯28at.com

let  final_value = if let Some(T) = Rust语句 {    //语句正确和成功的情况,如获取有效数据,将作为作用域的返回值赋值给final_value } else {    //错误或异常的情况的处理,如赋值为"",同样会作为作用域的返回值赋值给 final_value }

以下是一些常见的处理错误的方法:使用 match 表达式:通过使用 match 表达式,您可以根据 Result 类型的值来执行不同的操作。如果是 Ok 类型,可以提取成功的值;如果是 Err 类型,可以处理错误。使用 map_err() 方法:map_err() 方法可以将 Err 类型的值转换为另一种错误类型,并返回一个新的 Result 类型。使用 and_then() 或 or_else() 方法:这些方法可以在成功或失败的情况下执行不同的操作,并返回一个新的 Result 类型。zEN28资讯网——每日最新资讯28at.com

(3) 使用特定的函数:and_then() 和 or_else()和 ok_or()zEN28资讯网——每日最新资讯28at.com

这3个函数在Rust中的术语为组合算子,如果你已理解C/C++中的 && 和 ||或 Python中的and以及or语法的意义,那么你大概已经理解了 and_then() 这3个函数的意思。比如 and_then()是当调用者为true或调用者为正常的时候,才会调用and_then(...)函数。那么对于错误处理就非常有用。zEN28资讯网——每日最新资讯28at.com

下面的代码例子演示了烹饪的逻辑:当有食材的时候,才能按照食谱制作好菜品。隐含的意思就是(错误的情况下),没有食材的情况下,就不用照着食谱做菜了。zEN28资讯网——每日最新资讯28at.com

#![allow(dead_code)]#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }// 我们没有原材料(ingredient)来制作寿司。fn have_ingredients(food: Food) -> Option<Food> {    match food {        Food::Sushi => None,        _           => Some(food),    }}// 我们拥有全部食物的食谱,除了欠缺高超的烹饪手艺。fn have_recipe(food: Food) -> Option<Food> {    match food {        Food::CordonBleu => None,        _                => Some(food),    }}// 做一份好菜,我们需要原材料和食谱这两者。// 我们可以借助一系列 `match` 来表达相应的逻辑:// (原文:We can represent the logic with a chain of `match`es:)fn cookable_v1(food: Food) -> Option<Food> {    match have_ingredients(food) {        None       => None,        Some(food) => match have_recipe(food) {            None       => None,            Some(food) => Some(food),        },    }}// 这可以使用 `and_then()` 方便重写出更紧凑的代码:fn cookable_v2(food: Food) -> Option<Food> {    have_ingredients(food).and_then(have_recipe)}fn eat(food: Food, day: Day) {    match cookable_v2(food) {        Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food),        None       => println!("Oh no. We don't get to eat on {:?}?", day),    }}fn main() {    let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi);    eat(cordon_bleu, Day::Monday);    eat(steak, Day::Tuesday);    eat(sushi, Day::Wednesday);}

本文链接:http://www.28at.com/showinfo-26-50775-0.html入门Rust的固定套路:错误处理模式有三大类,帮你总结了

声明:本网页内容旨在传播知识,不代表本站观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。

显示全文

上一篇:系统设计小抄 - 如何做到高可用、高吞吐、高扩展性

下一篇:用了这个库,真的可以丢掉任务管理器了

最新热点