最近在学习总结Rust的各种场景的语法模式,也就是Rust写代码的模式。
今天分享关于Rust的错误处理的三大类语法模式。
第一类:有意不处理错误,忽略错误
第二类:对错误做自定义信息提示
第三类:推荐!根据正确和错误情况分开处理,错误还可以进一步分流处理
Rust的错误处理方式比起Golang更灵活,可以针对错误和当下代码需要赋值前做错误判断、故意忽略异常、异常时打印错误并终止等的不同编码场景,选用不同的语法模式。
错误处理涉及到数据类型、错误处理的控制语法、相关的crate模块。
Result属于Rust的核心crate提供的功能(core::result::Result)。Result 有2个特点:
首先,Result<T,E> 属于泛型,所以T和E可以是任意类型。但常见的使用模式是以T存储正常情况的信息,E存储错误和异常情况的信息。
其次,Result是枚举类型,其内部实际只包含:
Ok()Err()
这两个枚举,所以它的实例只会是Ok()或Err()之一。
Result的代码实现中就用的枚举类型的,代码如下:
/// 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。
总结一下Result<Ok(),Err()>: result 类型通常用于表示可能成功或失败的操作的结果。它使用 Result<T, E> 类型来表示,其中 T 是成功时的返回类型,E 是失败时的错误类型。
Option也是枚举类型,其内部实际只包含两种值:Some()和None,这个可以从其代码中得到印证。Option的实现代码为:
/// 代码文件 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),}
首先,从名称上理解一下:
其次,它们之间可以互相转化。对于错误处理来说,match处理Result或Option时,都要处理错误或None等异常值,就实现了Rust的最重要的错误处理逻辑。
(1) unwrap()
在 Rust 中,unwrap() 方法用于从 Result 类型中提取成功时的返回值。如果 Result 类型的值是 Ok(表示成功),则 unwrap() 方法将返回 T;如果 Result 类型的值是 Err(表示失败),则 unwrap() 方法将触发一个 panic,抛出一个 E 类型的错误。如果您在调用 unwrap() 方法时遇到错误,说明您正在处理一个 Err 类型的值,即失败的情况。在这种情况下,您应该使用其他方法来处理错误,而不是直接使用 unwrap() 方法。总之,在处理 Result 类型时,应该始终考虑可能的失败情况,并使用适当的方法来处理错误。直接使用 unwrap() 方法可能会导致程序崩溃(panic),因此应该谨慎使用。只应该在非生产的代码中使用。
(2) .fn()?符号
这个符号在Rust中的术语是“提前返回选项”(early return option),作用等同于unwrap()。只允许用于返回Result<>或Option<>类型的函数之后。在rust的早期版本中,有个try!宏具有等效的功能。
代码演示:(演示打开文件,返回Result<File, Error> 类型的值,然后可以被main()中的match方式处理):
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的内部函数,原始定义为:
pub fn open<P>(path: P) -> io::Result<File>
而演示代码中有意忽略了错误的情况,以?结尾,来调用open()函数:
let mut file = File::open(file_path)?;
expect() 可作为代替unwrap()或 ? 相比unwrap(), expect()是一个更好的选择,因为它允许发生错误时打印一个简单的消息并终止运行。
根据正确和错误情况分开处理,错误还可以进一步分流处理。
(1) match,根据正确和错误情况分开处理。
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), }}
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() 将在以后发布的文章中再讲解。本文不做详细介绍。
(2) if let,适合直接在赋值前做错误处理。代码模式为:
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 类型。
(3) 使用特定的函数:and_then() 和 or_else()和 ok_or()
这3个函数在Rust中的术语为组合算子,如果你已理解C/C++中的 && 和 ||或 Python中的and以及or语法的意义,那么你大概已经理解了 and_then() 这3个函数的意思。比如 and_then()是当调用者为true或调用者为正常的时候,才会调用and_then(...)函数。那么对于错误处理就非常有用。
下面的代码例子演示了烹饪的逻辑:当有食材的时候,才能按照食谱制作好菜品。隐含的意思就是(错误的情况下),没有食材的情况下,就不用照着食谱做菜了。
#![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的固定套路:错误处理模式有三大类,帮你总结了
声明:本网页内容旨在传播知识,不代表本站观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。