Rust错误处理本文同步于Rust中文社区专栏文章:Rust错误处理 ,本文时间:2018-12-14, 译者:krircc,简介:天青色,原文出处欢迎向Rust中文社区投稿,投稿地址 ,好文将在以下地方直接展示Rust中文社区首页Rust中文社区文章栏目知乎专栏Rust中文社区思否专栏Rust中文社区简书专题Rust中文社区微博Rustlang-cn智能编译器Rust编译器最重要的工作是防止Rust程序中的错误。如果代码没有正确遵循内存管理规则或生命周期注释,它会在编译时分析代码并发出警告。例如,#[allow(unused_variables)] //???? A lint attribute used to suppress the warning; unused variable: bfn main() { let a = vec![1, 2, 3]; let b = a; println!("{:?}", a);}// —— Compile time error ——error[E0382]: use of moved value: a –> src/main.rs:6:22 |3 | let b = a; | - value moved here4 |5 | println!("{:?}", a); | ^ value used here after move | = note: move occurs because a has type std::vec::Vec<i32>, which does not implement the Copy traiterror: aborting due to previous errorFor more information about this error, try rustc --explain E0382.// ⭐ instead using #[allow(unused_variables)], consider using “let b = a;” in line 4. // Also you can use “let _ =” to completely ignore return valuesRust编译器不仅检查与生命周期或内存管理相关的问题,还检查常见的编码错误,如下面的代码。struct Color { r: u8, g: u8, b: u8,}fn main() { let yellow = Color { r: 255, g: 255, d: 0, }; println!(“Yellow = rgb({},{},{})”, yellow.r, yellow.g, yellow.b);}// ———— Compile time error ————error[E0560]: struct Color has no field named d –> src/main.rs:11:9 |11 | d: 0, | ^ field does not exist - did you mean b?error: aborting due to previous errorFor more information about this error, try rustc --explain E0560.以上错误消息非常具有描述性,我们可以很容易地看出错误在哪里。但是,虽然我们无法通过错误消息识别问题,但rustc –explain 命令通过显示表达相同问题的简单代码示例以及我们必须使用的解决方案来帮助我们识别错误类型以及如何解决它。例如,在控制台中显示以下输出。rustc –explain E0571// A break statement with an argument appeared in a non-loop loop.// Example of erroneous code:let result = while true { if satisfied(i) { break 2i; // error: break with value from a while loop } i += 1;};// The break statement can take an argument (which will be the value of the loop expression if the break statement is executed) in loop loops, but not for, while, or while let loops.Make sure break value; statements only occur in loop loops:let result = loop { // ok! if satisfied(i) { break 2i; } i += 1;};????您也可以通过Rust Compiler Error Index阅读相同的解释 。例如,要检查E0571错误的解释,您可以使用https://doc.rust-lang.org/error-index.html#E0571 Panickingpanic!()▸ 在某些情况下,当发生错误时,我们无法做任何事情来处理它,如果错误是某种情况,那就不应该发生。换句话说,如果这是一个不可恢复的错误。▸ 当我们不使用功能丰富的调试器或正确的日志时,有时我们需要通过打印特定的消息或变量绑定的值从特定的代码行退出程序来调试代码以了解当前的程序的流程。对于上述情况,我们可以使用panic!宏。让我们看几个例子。⭐ panic!() 运行基于线程。一个线程可能会被恐慌,而其他线程正在运行。01.从特定行退出。fn main() { // some code // if we need to debug in here panic!();}// ————– Compile time error ————–thread ‘main’ panicked at ’explicit panic’, src/main.rs:5:502.退出并显示自定义错误消息。#[allow(unused_mut)] // ???? A lint attribute used to suppress the warning; username variable does not need to be mutablefn main() { let mut username = String::new(); // some code to get the name if username.is_empty() { panic!(“Username is empty!”); } println!("{}", username);}// ————– Compile time error ————–thread ‘main’ panicked at ‘Username is empty!’, src/main.rs:8:903.退出附带代码元素的值。#[derive(Debug)] // ???? A lint attribute which use to implement std::fmt::Debug to Colorstruct Color { r: u8, g: u8, b: u8,}#[allow(unreachable_code)] // ???? A lint attribute used to suppress the warning; unreachable statementfn main() { let some_color: Color; // some code to get the color. ex some_color = Color {r: 255, g: 255, b: 0}; // if we need to debug in here panic!("{:?}", some_color); println!( “The color = rgb({},{},{})”, some_color.r, some_color.g, some_color.b );}// ————– Compile time error ————–thread ‘main’ panicked at ‘Color { r: 255, g: 255, b: 0 }’, src/main.rs:16:5正如您在上面的示例中所看到的,panic!()支持println!()类型样式参数 。默认情况下,它会输出错误消息,文件路径以及发生错误的行号和列号。unimplemented!()如果您的代码具有未完成的代码段,则有一个标准化宏unimplemented!()来标记这些路径。如果程序通过这些路径运行,程序将panicked并返回"not yet implemented"的错误消息。// error messages with panic!()thread ‘main’ panicked at ’explicit panic’, src/main.rs:6:5thread ‘main’ panicked at ‘Username is empty!’, src/main.rs:9:9thread ‘main’ panicked at ‘Color { r: 255, g: 255, b: 0 }’, src/main.rs:17:5// error messages with unimplemented!()thread ‘main’ panicked at ’not yet implemented’, src/main.rs:6:5thread ‘main’ panicked at ’not yet implemented: Username is empty!’, src/main.rs:9:9thread ‘main’ panicked at ’not yet implemented: Color { r: 255, g: 255, b: 0 }’, src/main.rs:17:5unreachable!()这是标记程序不应输入的路径的标准宏。如果程序进入这些路径,程序将panicked并返回"‘internal error: entered unreachable code’“错误消息。fn main() { let level = 22; let stage = match level { 1…5 => “beginner”, 6…10 => “intermediate”, 11…20 => “expert”, _ => unreachable!(), }; println!(”{}", stage);}// ————– Compile time error ————–thread ‘main’ panicked at ‘internal error: entered unreachable code’, src/main.rs:7:20我们也可以为此设置自定义错误消息。// — with a custom message — => unreachable!(“Custom message”),// ————– Compile time error ————–thread ‘main’ panicked at ‘internal error: entered unreachable code: Custom message’, src/main.rs:7:20// — with debug data —_ => unreachable!(“level is {}”, level),// ————– Compile time error ————–thread ‘main’ panicked at ‘internal error: entered unreachable code: level is 22’, src/main.rs:7:14assert!(), assert_eq!(), assert_ne!()这些是标准宏,通常与测试断言一起使用。assert!()确保布尔表达式为true。如果表达式为false,则会发生panics。fn main() { let f = false; assert!(f)}// ————– Compile time error ————–thread ‘main’ panicked at ‘assertion failed: f’, src/main.rs:4:5assert_eq!()确保两个表达式相等。如果表达式不相等则会发生panics。fn main() { let a = 10; let b = 20; assert_eq!(a, b);}// ————– Compile time error ————–thread ‘main’ panicked at ‘assertion failed: (left == right) left: 10, right: 20’, src/main.rs:5:5assert_ne!()确保两个表达式不相等。如果表达式相等,它会发生panics。fn main() { let a = 10; let b = 10; assert_ne!(a, b);}// ————– Compile time error ————–thread ‘main’ panicked at ‘assertion failed: (left != right) left: 10, right: 10’, src/main.rs:5:5⭐使用表达式assert_ne!()和assert_eq!()应返回相同的数据类型。我们也可以为这些宏设置自定义错误消息。举些例子,带有自定义消息 assert_eq!()fn main() { let a = 10; let b = 20; assert_eq!(a, b, “a and b should be equal”);}// ————– Compile time error ————–thread ‘main’ panicked at ‘assertion failed: (left == right) left: 10, right: 20: a and b should be equal’, src/main.rs:5:5assert_eq!()带有调试数据fn main() { let a = 10; let b = 20; let c = 40; assert_eq!(a+b, c, “a = {} ; b = {}”, a, b);}// ————– Compile time error ————–thread ‘main’ panicked at ‘assertion failed: (left == right) left: 30, right: 40: a = 10 ; b = 20’, src/main.rs:7:5debug_assert!(), debug_assert_eq!(), debug_assert_ne!()????这些与上面的assert宏类似。但默认情况下,这些语句仅在非优化构建中启用。debug_assert除非我们传递-C debug-assertions给编译器,否则在发布版本中将省略所有这些宏。Option and Result许多语言使用null\ nil\ undefined 类型来表示空输出和Exceptions处理错误。Rust会同时使用两者,特别是为了防止诸如空指针异常,异常等敏感数据泄漏等问题。相反,Rust提供了两个特殊的通用枚举 ; Option和Result处理上述案件。如您所知:▸ Option可以包含某个值Some或没有值/ None。▸ Result可以表示成功/ Ok 或失败/Err。// An output can have either Some value or no value/ None.enum Option<T> { // T is a generic and it can contain any type of value. Some(T), None,}// A result can represent either success/ Ok or failure/ Err.enum Result<T, E> { // T and E are generics. T can contain any type of value, E can be any error. Ok(T), Err(E),}Option的基本用法编写函数或数据类型时:如果函数的参数是可选的,如果函数为非空,并且返回的输出可以为空,如果数据类型的属性的值可以是空,我们不得不使用他们的数据类型为Option类型例如,如果函数输出一个&str值并且输出可以为空,则函数的返回类型应设置为Option<&str>fn get_an_optional_value() -> Option<&str> { //if the optional value is not empty return Some(“Some value”); //else None}同样,如果数据类型的属性值可以为空或者像下面示例中middle_name的Name数据类型那样可选,我们应该将其数据类型设置为Option类型。struct Name { first_name: String, middle_name: Option<String>, // middle_name can be empty last_name: String,}????如您所知,我们可以使用模式匹配match来捕获相关的返回类型(Some/ None) 。有一个函数来获取当前用户的主目录在std::env为home_dir() 。由于所有用户在Linux等系统中都没有主目录,因此用户的主目录可以是可选的。所以它返回一个Option类型; Option<PathBuf>.use std::env;fn main() { let home_path = env::home_dir(); match home_path { Some(p) => println!("{:?}", p), // This prints “/root”, if you run this in Rust playground None => println!(“Can not find the home directory!”), }}⭐但是,当在函数中使用可选参数时,我们必须None在调用函数时传递空参数的值。fn get_full_name(fname: &str, lname: &str, mname: Option<&str>) -> String { // middle name can be empty match mname { Some(n) => format!("{} {} {}", fname, n, lname), None => format!("{} {}", fname, lname), }}fn main() { println!("{}", get_full_name(“Galileo”, “Galilei”, None)); println!("{}", get_full_name(“Leonardo”, “Vinci”, Some(“Da”)));}// ???? Better create a struct as Person with fname, lname, mname fields and create a impl function as full_name()????除此之外,Option类型与Rust中的可空指针一起使用。由于Rust中没有空指针,因此指针类型应指向有效位置。因此,如果指针可以为空,我们就可以使用了Option<Box<T>> 。Result的基本用法如果函数可以产生错误,我们必须Result通过组合有效输出的数据类型和错误的数据类型来使用类型。例如,如果有效输出的数据类型为u64且错误类型为String ,则返回类型应为Result<u64, String> 。fn function_with_error() -> Result<u64, String> { //if error happens return Err(“The error message”.to_string()); // else, return valid output Ok(255)}????如您所知,我们可以使用模式匹配match来捕获相关的返回类型(Ok/ Err)。有一个函数可以获取std::env 任何环境变量中的值是var() 。它的输入是环境变量名称。如果我们传递了错误的环境变量,或者程序在运行时无法提取环境变量的值,则会产生错误。所以它的返回类型是一种Result类型; Result<String, VarError>.use std::env;fn main() { let key = “HOME”; match env::var(key) { Ok(v) => println!("{}", v), // This prints “/root”, if you run this in Rust playground Err(e) => println!("{}", e), // This prints “environment variable not found”, if you give a nonexistent environment variable }}is_some(), is_none(), is_ok(), is_err()除了match表情,rust还提供is_some() ,is_none()并且is_ok() ,is_err()功能,以确定返回类型。fn main() { let x: Option<&str> = Some(“Hello, world!”); assert_eq!(x.is_some(), true); assert_eq!(x.is_none(), false); let y: Result<i8, &str> = Ok(10); assert_eq!(y.is_ok(), true); assert_eq!(y.is_err(), false);}ok(), err() for Result typesrust另外提供ok()和err()为Result类型。它们将Result类型的Ok<T>值和Err<E>值转换为Option类型。fn main() { let o: Result<i8, &str> = Ok(8); let e: Result<i8, &str> = Err(“message”); assert_eq!(o.ok(), Some(8)); // Ok(v) ok = Some(v) assert_eq!(e.ok(), None); // Err(v) ok = None assert_eq!(o.err(), None); // Ok(v) err = None assert_eq!(e.err(), Some(“message”)); // Err(v) err = Some(v)}Unwrap and Expectunwrap()▸如果Option类型具有Some值或Result类型具有Ok值,则其中的值将传递到下一步。▸如果Option类型具有None值或Result类型具有Err值,则编程panics ; 如果Err,panics携带错误消息。该功能与以下代码类似,使用match而不是使用unwrap() 。示例使用Option和matchfn main() { let x; match get_an_optional_value() { Some(v) => x = v, // if Some(“abc”), set x to “abc” None => panic!(), // if None, panic without any message } println!("{}", x); // “abc” ; if you change line 14 false to true}fn get_an_optional_value() -> Option<&‘static str> { //if the optional value is not empty if false { return Some(“abc”); } //else None}// ————— Compile time error —————thread ‘main’ panicked at ’explicit panic’, src/main.rs:5:17示例使用Result和matchfn main() { let x; match function_with_error() { Ok(v) => x = v, // if Ok(255), set x to 255 Err(e) => panic!(e), // if Err(“some message”), panic with error message “some message” } println!("{}", x); // 255 ; if you change line 13 true to false}fn function_with_error() -> Result<u64, String> { //if error happens if true { return Err(“some message”.to_string()); } // else, return valid output Ok(255)}// ———- Compile time error ———-thread ‘main’ panicked at ‘some message’, src/main.rs:5:19上述main函数中的相同代码可以使用unwrap()两行来编写。// 01. unwrap error message for Nonefn main() { let x = get_an_optional_value().unwrap(); println!("{}", x);}// ————— Compile time error —————thread ‘main’ panicked at ‘called Option::unwrap() on a None value’, libcore/option.rs:345:21// 02. unwrap error message for Errfn main() { let x = function_with_error().unwrap(); println!("{}", x);}// ————— Compile time error —————thread ‘main’ panicked at ‘called Result::unwrap() on an Err value: “some message”’, libcore/result.rs:945:5⭐但是正如您所看到的,当使用unwrap()错误消息时,没有显示发生恐慌的确切行号。expect()类似unwrap()但可以为恐慌设置自定义消息。// 01. expect error message for Nonefn main() { let n: Option<i8> = None; n.expect(“empty value returned”);}// ————— Compile time error —————thread ‘main’ panicked at ’empty value returned’, libcore/option.rs:989:5// 02. expect error message for Errfn main() { let e: Result<i8, &str> = Err(“some message”); e.expect(“expect error message”);}// ————— Compile time error —————thread ‘main’ panicked at ’expect error message: “some message”’, libcore/result.rs:945:5unwrap_err() and expect_err() for Result typesunwrap()和expect()相反的情况; Ok时恐慌而不是Err时。两者都在Ok错误消息中打印内部值。????通常用于测试。// 01. unwrap_err error message for Okfn main() { let o: Result<i8, &str> = Ok(8); o.unwrap_err();}// ———- Compile time error ———-thread ‘main’ panicked at ‘called Result::unwrap_err() on an Ok value: 8’, libcore/result.rs:945:5// 02. expect_err error message for Okfn main() { let o: Result<i8, &str> = Ok(8); o.expect_err(“Should not get Ok value”);}// ———- Compile time error ———-thread ‘main’ panicked at ‘Should not get Ok value: 8’, libcore/result.rs:945:5unwrap_or(), unwrap_or_default() and unwrap_or_else()????这些有点类似于unwrap(),如果Option类型有Some值或Result类型有Ok值,则它们内部的值传递到下一步。但是当有None 或者 Err,功能有点不同。unwrap_or() :使用None或Err,您传递给的unwrap_or()值将传递到下一步。但是,您传递的值的数据类型应与相关Some或Ok的数据类型匹配。fn main() { let v1 = 8; let v2 = 16; let s_v1 = Some(8); let n = None; assert_eq!(s_v1.unwrap_or(v2), v1); // Some(v1) unwrap_or v2 = v1 assert_eq!(n.unwrap_or(v2), v2); // None unwrap_or v2 = v2 let o_v1: Result<i8, &str> = Ok(8); let e: Result<i8, &str> = Err(“error”); assert_eq!(o_v1.unwrap_or(v2), v1); // Ok(v1) unwrap_or v2 = v1 assert_eq!(e.unwrap_or(v2), v2); // Err unwrap_or v2 = v2}unwrap_or_default() :使用None或Err,相关的数据类型的默认值Some或者Ok,传递到下一步。fn main() { let v = 8; let v_default = 0; let s_v: Option<i8> = Some(8); let n: Option<i8> = None; assert_eq!(s_v.unwrap_or_default(), v); // Some(v) unwrap_or_default = v assert_eq!(n.unwrap_or_default(), v_default); // None unwrap_or_default = default value of v let o_v: Result<i8, &str> = Ok(8); let e: Result<i8, &str> = Err(“error”); assert_eq!(o_v.unwrap_or_default(), v); // Ok(v) unwrap_or_default = v assert_eq!(e.unwrap_or_default(), v_default); // Err unwrap_or_default = default value of v}unwrap_or_else() :类似于unwrap_or()。唯一的区别是,您必须传递一个闭包,它返回一个具有Some或Ok相关数据类型的值,而不是传递一个值。fn main() { let v1 = 8; let v2 = 16; let s_v1 = Some(8); let n = None; let fn_v2_for_option = || 16; assert_eq!(s_v1.unwrap_or_else(fn_v2_for_option), v1); // Some(v1) unwrap_or_else fn_v2 = v1 assert_eq!(n.unwrap_or_else(fn_v2_for_option), v2); // None unwrap_or_else fn_v2 = v2 let o_v1: Result<i8, &str> = Ok(8); let e: Result<i8, &str> = Err(“error”); let fn_v2_for_result = || 16; assert_eq!(o_v1.unwrap_or_else(fn_v2_for_result), v1); // Ok(v1) unwrap_or_else fn_v2 = v1 assert_eq!(e.unwrap_or_else(fn_v2_for_result), v2); // Err unwrap_or_else fn_v2 = v2}Error and None Propagation我们应该使用恐慌panic!(),unwrap(),expect()只有当我们没有一个更好处理办法的情况。此外如果一个函数包含表达式既能产生None也能产生Err, ▸我们可以在同一函数中处理▸我们可以立即返回None 和Err给调用者。因此调用者可以决定如何处理它们。???? None类型无需始终由函数的调用者处理。但Rusts处理Err类型的约定是,立即将它们返回给调用者,以便给调用者更多的控制权来决定如何处理它们。?操作符▸如果Option类型具有Some值或Result类型具有Ok值,则其中的值将传递到下一步。▸如果Option类型具有None值或Result类型具有Err值,则立即将它们返回给函数的调用者。示例Option类型,fn main() { if complex_function().is_none() { println!(“X not exists!”); }}fn complex_function() -> Option<&‘static str> { let x = get_an_optional_value()?; // if None, returns immidiately; if Some(“abc”), set x to “abc” // some other code, ex println!("{}", x); // “abc” ; if you change line 19 false to true Some("")}fn get_an_optional_value() -> Option<&‘static str> { //if the optional value is not empty if false { return Some(“abc”); } //else None}示例Result类型,fn main() { // main function is the caller of complex_function function // So we handle errors of complex_function(), inside main() if complex_function().is_err() { println!(“Can not calculate X!”); }}fn complex_function() -> Result<u64, String> { let x = function_with_error()?; // if Err, returns immidiately; if Ok(255), set x to 255 // some other code, ex println!("{}", x); // 255 ; if you change line 20 true to false Ok(0)}fn function_with_error() -> Result<u64, String> { //if error happens if true { return Err(“some message”.to_string()); } // else, return valid output Ok(255)}从main()传播错误在Rust版本1.26之前,我们无法从main()函数传播Result和Option。但是现在,我们可以从main()函数中传播Result类型,并打印出Err的Debug表示形式。use std::fs::File;fn main() -> std::io::Result<()> { let _ = File::open(“not-existing-file.txt”)?; Ok(()) // Because of the default return value of Rust functions is an empty tuple/ ()}// Because of the program can not find not-existing-file.txt , it produces,// Err(Os { code: 2, kind: NotFound, message: “No such file or directory” })// While propagating error, the program prints,// Error: Os { code: 2, kind: NotFound, message: “No such file or directory” }Combinators让我们看看组合器是什么,“组合者”的一个含义是更加非正式的意义,指的是组合模式,一种以组合事物的思想为中心组织图书馆的风格。通常存在一些类型T,一些用于构造类型T的“原始”值的函数,以及一些可以以各种方式组合类型T的值以构建类型T的更复杂值的 “ 组合器 ” 。另一个定义是“ 没有自由变量的函数 ”(wiki.haskell.org)组合子是一个函数,其从程序片段构建程序片段 ; 从某种意义上说,使用组合器的程序员自动构建了大部分所需的程序,而不是手工编写每个细节。Rust生态系统中“组合子”的确切定义有点不清楚。▸ or(),and(),or_else(),and_then()组合类型为T的两个值并返回相同类型T。▸ filter()对于Option类型使用闭包作为条件函数来过滤类型T.返回相同的类型T.▸ map(),map_err()通过使用闭包转换类型T。可以更改T内部值的数据类型。例如:Some<&str>可转化为Some<usize>或者Err<&str>可转化为Err<isize>等▸ map_or(),map_or_else()通过应用闭包转换类型T并返回类型T内的值。对None 和Err,应用默认值或其他闭包。▸ ok_or(),ok_or_else()对于Option类型将Option类型转换为Result类型。▸ as_ref(),as_mut()将类型T转换为引用或可变引用。or() and and()组合两个表达式返回Option/ Result▸or() :如果任何一个得到Some或Ok`,该值立即返回。▸ and() :如果两者都得到Some或Ok,则返回第二个表达式中的值。如果任何一个获得None或Err该值立即返回。fn main() { let s1 = Some(“some1”); let s2 = Some(“some2”); let n: Option<&str> = None; let o1: Result<&str, &str> = Ok(“ok1”); let o2: Result<&str, &str> = Ok(“ok2”); let e1: Result<&str, &str> = Err(“error1”); let e2: Result<&str, &str> = Err(“error2”); assert_eq!(s1.or(s2), s1); // Some1 or Some2 = Some1 assert_eq!(s1.or(n), s1); // Some or None = Some assert_eq!(n.or(s1), s1); // None or Some = Some assert_eq!(n.or(n), n); // None1 or None2 = None2 assert_eq!(o1.or(o2), o1); // Ok1 or Ok2 = Ok1 assert_eq!(o1.or(e1), o1); // Ok or Err = Ok assert_eq!(e1.or(o1), o1); // Err or Ok = Ok assert_eq!(e1.or(e2), e2); // Err1 or Err2 = Err2 assert_eq!(s1.and(s2), s2); // Some1 and Some2 = Some2 assert_eq!(s1.and(n), n); // Some and None = None assert_eq!(n.and(s1), n); // None and Some = None assert_eq!(n.and(n), n); // None1 and None2 = None1 assert_eq!(o1.and(o2), o2); // Ok1 and Ok2 = Ok2 assert_eq!(o1.and(e1), e1); // Ok and Err = Err assert_eq!(e1.and(o1), e1); // Err and Ok = Err assert_eq!(e1.and(e2), e1); // Err1 and Err2 = Err1}????nightly支持Option类型的xor(),它返回Some当只有一个表达式返回Some,而不是两个。or_else()类似于or()。唯一的区别是,第二个表达式应该是一个返回相同类型T 的闭包。fn main() { // or_else with Option let s1 = Some(“some1”); let s2 = Some(“some2”); let fn_some = || Some(“some2”); // similar to: let fn_some = || -> Option<&str> { Some(“some2”) }; let n: Option<&str> = None; let fn_none = || None; assert_eq!(s1.or_else(fn_some), s1); // Some1 or_else Some2 = Some1 assert_eq!(s1.or_else(fn_none), s1); // Some or_else None = Some assert_eq!(n.or_else(fn_some), s2); // None or_else Some = Some assert_eq!(n.or_else(fn_none), None); // None1 or_else None2 = None2 // or_else with Result let o1: Result<&str, &str> = Ok(“ok1”); let o2: Result<&str, &str> = Ok(“ok2”); let fn_ok = || Ok(“ok2”); // similar to: let fn_ok = || -> Result<&str, &str> { Ok(“ok2”) }; let e1: Result<&str, &str> = Err(“error1”); let e2: Result<&str, &str> = Err(“error2”); let fn_err = || Err(“error2”); assert_eq!(o1.or_else(fn_ok), o1); // Ok1 or_else Ok2 = Ok1 assert_eq!(o1.or_else(fn_err), o1); // Ok or_else Err = Ok assert_eq!(e1.or_else(fn_ok), o2); // Err or_else Ok = Ok assert_eq!(e1.or_else(fn_err), e2); // Err1 or_else Err2 = Err2}and_then()类似于and()。唯一的区别是,第二个表达式应该是一个返回相同类型T 的闭包。fn main() { // and_then with Option let s1 = Some(“some1”); let s2 = Some(“some2”); let fn_some = || Some(“some2”); // similar to: let fn_some = || -> Option<&str> { Some(“some2”) }; let n: Option<&str> = None; let fn_none = || None; assert_eq!(s1.and_then(fn_some), s2); // Some1 and_then Some2 = Some2 assert_eq!(s1.and_then(fn_none), n); // Some and_then None = None assert_eq!(n.and_then(fn_some), n); // None and_then Some = None assert_eq!(n.and_then(fn_none), n); // None1 and_then None2 = None1 // and_then with Result let o1: Result<&str, &str> = Ok(“ok1”); let o2: Result<&str, &str> = Ok(“ok2”); let fn_ok = || Ok(“ok2”); // similar to: let fn_ok = || -> Result<&str, &str> { Ok(“ok2”) }; let e1: Result<&str, &str> = Err(“error1”); let e2: Result<&str, &str> = Err(“error2”); let fn_err = || Err(“error2”); assert_eq!(o1.and_then(fn_ok), o2); // Ok1 and_then Ok2 = Ok2 assert_eq!(o1.and_then(fn_err), e2); // Ok and_then Err = Err assert_eq!(e1.and_then(fn_ok), e1); // Err and_then Ok = Err assert_eq!(e1.and_then(fn_err), e1); // Err1 and_then Err2 = Err1}filter()????通常在编程语言中,filter函数与数组或迭代器一起使用,通过函数/闭包过滤自己的元素来创建新的数组/迭代器。Rust还提供了一个filter()迭代器适配器,用于在迭代器的每个元素上应用闭包,将其转换为另一个迭代器。然而,在这里,我们正在谈论filter()函数与Option类型。仅当我们传递一个Some值并且给定的闭包为它返回true时,返回相同的Some类型。如果None传递类型或闭包返回false,返回None。闭包使用Some里面的值作为参数。Rust仍然支持filter()只支持Option的类型。fn main() { let s1 = Some(3); let s2 = Some(6); let n = None; let fn_is_even = |x: &i8| x % 2 == 0; assert_eq!(s1.filter(fn_is_even), n); // Some(3) -> 3 is not even -> None assert_eq!(s2.filter(fn_is_even), s2); // Some(6) -> 6 is even -> Some(6) assert_eq!(n.filter(fn_is_even), n); // None -> no value -> None}map() and map_err()????通常在编程语言中,map()函数与数组或迭代器一起使用,以在数组或迭代器的每个元素上应用闭包。Rust还提供了一个map()迭代器适配器,用于在迭代器的每个元素上应用闭包,将其转换为另一个迭代器。但是在这里我们讨论的是map()函数与Option和Result类型。map() :通过应用闭包来转换类型T. 可以根据闭包的返回类型更改Some或Ok块数据类型。转换Option<T>为Option<U> ,转换Result<T, E>为Result<U, E>⭐ map(),仅仅 Some和Ok值改变。对Err内部值没有影响(None根本不包含任何值)。fn main() { let s1 = Some(“abcde”); let s2 = Some(5); let n1: Option<&str> = None; let n2: Option<usize> = None; let o1: Result<&str, &str> = Ok(“abcde”); let o2: Result<usize, &str> = Ok(5); let e1: Result<&str, &str> = Err(“abcde”); let e2: Result<usize, &str> = Err(“abcde”); let fn_character_count = |s: &str| s.chars().count(); assert_eq!(s1.map(fn_character_count), s2); // Some1 map = Some2 assert_eq!(n1.map(fn_character_count), n2); // None1 map = None2 assert_eq!(o1.map(fn_character_count), o2); // Ok1 map = Ok2 assert_eq!(e1.map(fn_character_count), e2); // Err1 map = Err2}map_err()对于Result类型:Err块的数据类型可以根据闭包的返回类型进行更改。转换Result<T, E>为Result<T, F>。⭐map_err(),只有Err值会发生变化。对Ok内部的值没有影响。fn main() { let o1: Result<&str, &str> = Ok(“abcde”); let o2: Result<&str, isize> = Ok(“abcde”); let e1: Result<&str, &str> = Err(“404”); let e2: Result<&str, isize> = Err(404); let fn_character_count = |s: &str| -> isize { s.parse().unwrap() }; // convert str to isize assert_eq!(o1.map_err(fn_character_count), o2); // Ok1 map = Ok2 assert_eq!(e1.map_err(fn_character_count), e2); // Err1 map = Err2}map_or() and map_or_else()这些功能也与unwrap_or()和unwrap_or_else()相似。但是map_or()和map_or_else()在Some,Ok值上应用闭包和返回类型T内的值。map_or() :仅支持Option类型(不支持Result)。将闭包应用于Some内部值并根据闭包返回输出。为None类型返回给定的默认值。fn main() { const V_DEFAULT: i8 = 1; let s = Some(10); let n: Option<i8> = None; let fn_closure = |v: i8| v + 2; assert_eq!(s.map_or(V_DEFAULT, fn_closure), 12); assert_eq!(n.map_or(V_DEFAULT, fn_closure), V_DEFAULT);}map_or_else() :支持两种Option和Result类型(Result仅限nightly)。类似map_or()但应该提供另一个闭包而不是第一个参数的默认值。⭐ None类型不包含任何值。所以不需要将任何东西传递给闭包作为输入Option类型。但是Err类型在其中包含一些值。因此,默认闭包应该能够将其作为输入读取,同时将其与Result类型一起使用。#![feature(result_map_or_else)] // enable unstable library feature ‘result_map_or_else’ on nightlyfn main() { let s = Some(10); let n: Option<i8> = None; let fn_closure = |v: i8| v + 2; let fn_default = || 1; // None doesn’t contain any value. So no need to pass anything to closure as input. assert_eq!(s.map_or_else(fn_default, fn_closure), 12); assert_eq!(n.map_or_else(fn_default, fn_closure), 1); let o = Ok(10); let e = Err(5); let fn_default_for_result = |v: i8| v + 1; // Err contain some value inside it. So default closure should able to read it as input assert_eq!(o.map_or_else(fn_default_for_result, fn_closure), 12); assert_eq!(e.map_or_else(fn_default_for_result, fn_closure), 6);}ok_or() and ok_or_else()如前所述ok_or(),ok_or_else()将Option类型转换为Result类型。Some对Ok和None对Err 。ok_or() :默认Err消息应作为参数传递.fn main() { const ERR_DEFAULT: &str = “error message”; let s = Some(“abcde”); let n: Option<&str> = None; let o: Result<&str, &str> = Ok(“abcde”); let e: Result<&str, &str> = Err(ERR_DEFAULT); assert_eq!(s.ok_or(ERR_DEFAULT), o); // Some(T) -> Ok(T) assert_eq!(n.ok_or(ERR_DEFAULT), e); // None -> Err(default)}ok_or_else() :类似于ok_or()。应该将闭包作为参数传递。fn main() { let s = Some(“abcde”); let n: Option<&str> = None; let fn_err_message = || “error message”; let o: Result<&str, &str> = Ok(“abcde”); let e: Result<&str, &str> = Err(“error message”); assert_eq!(s.ok_or_else(fn_err_message), o); // Some(T) -> Ok(T) assert_eq!(n.ok_or_else(fn_err_message), e); // None -> Err(default)}as_ref() and as_mut()????如前所述,这些函数用于借用类型T作为引用或作为可变引用。as_ref() :转换Option<T>到Option<&T>和Result<T, E>到Result<&T, &E>as_mut() :转换Option<T>到Option<&mut T>和Result<T, E>到Result<&mut T, &mut E>自定义错误类型Rust允许我们创建自己的Err类型。我们称之为“ 自定义错误类型”。Error trait如您所知,traits定义了类型必须提供的功能。但是我们不需要总是为常用功能定义新的特性,因为Rust 标准库提供了一些可以在我们自己的类型上实现的可重用特性。创建自定义错误类型时,std::error::Error trait可帮助我们将任何类型转换为Err类型。use std::fmt::{Debug, Display};pub trait Error: Debug + Display { fn source(&self) -> Option<&(Error + ‘static)> { … }}一个特质可以从另一个特质继承。trait Error: Debug + Display意味着Error特质继承fmt::Debug和fmt::Display特质。// traits inside Rust standard library core fmt module/ std::fmtpub trait Display { fn fmt(&self, f: &mut Formatter) -> Result<(), Error>;}pub trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<(), Error>;}▸ Display最终用户应如何将此错误视为面向消息/面向用户的输出。通常通过println!("{}")或打印eprintln!("{}")▸ Debug如何显示Errwhile调试/面向程序员的输出。通常打印println!("{:?}")或eprintln!("{:?}") 漂亮打印,可以使用println!("{:#?}")或eprintln!("{:#?}")。▸ source()此错误的较低级别来源(如果有)。可选的。首先,让我们看看如何std::error::Error在最简单的自定义错误类型上实现特征。use std::fmt;// Custom error type; can be any type which defined in the current crate// ???? In here, we use a simple “unit struct” to simplify the examplestruct AppError;// Implement std::fmt::Display for AppErrorimpl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, “An Error Occurred, Please Try Again!”) // user-facing output }}// Implement std::fmt::Debug for AppErrorimpl fmt::Debug for AppError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, “{{ file: {}, line: {} }}”, file!(), line!()) // programmer-facing output }}// A sample function to produce an AppError Errfn produce_error() -> Result<(), AppError> { Err(AppError)}fn main() { match produce_error() { Err(e) => eprintln!("{}", e), // An Error Occurred, Please Try Again! _ => println!(“No error”), } eprintln!("{:?}", produce_error()); // Err({ file: src/main.rs, line: 17 })}希望你理解要点。现在,让我们看一些带有错误代码和错误消息的自定义错误类型。use std::fmt;struct AppError { code: usize, message: String,}// Different error messages according to AppError.codeimpl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let err_msg = match self.code { 404 => “Sorry, Can not find the Page!”, _ => “Sorry, something is wrong! Please Try Again!”, }; write!(f, “{}”, err_msg) }}// A unique format for dubugging outputimpl fmt::Debug for AppError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, “AppError {{ code: {}, message: {} }}”, self.code, self.message ) }}fn produce_error() -> Result<(), AppError> { Err(AppError { code: 404, message: String::from(“Page not found”), })}fn main() { match produce_error() { Err(e) => eprintln!("{}", e), // Sorry, Can not find the Page! _ => println!(“No error”), } eprintln!("{:?}", produce_error()); // Err(AppError { code: 404, message: Page not found }) eprintln!("{:#?}", produce_error()); // Err( // AppError { code: 404, message: Page not found } // )}⭐️Rust标准库不仅提供了可重用的特性,而且还有助于通过#[derive]属性神奇地生成少数特征的实现。Rust支持derive std::fmt::Debug,为调试消息提供默认格式。因此,我们可以在struct前声明使用#[derive(Debug)]跳过实现std::fmt::Debug 自定义错误类型。use std::fmt;#[derive(Debug)] // derive std::fmt::Debug on AppErrorstruct AppError { code: usize, message: String,}impl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let err_msg = match self.code { 404 => “Sorry, Can not find the Page!”, _ => “Sorry, something is wrong! Please Try Again!”, }; write!(f, “{}”, err_msg) }}fn produce_error() -> Result<(), AppError> { Err(AppError { code: 404, message: String::from(“Page not found”), })}fn main() { match produce_error() { Err(e) => eprintln!("{}", e), // Sorry, Can not find the Page! _ => println!(“No error”), } eprintln!("{:?}", produce_error()); // Err(AppError { code: 404, message: Page not found }) eprintln!("{:#?}", produce_error()); // Err( // AppError { // code: 404, // message: “Page not found” // } // )}From trait在编写真实的程序时,我们大多数时候必须同时处理不同的模块,不同的std和第三方的板条箱。但是每个包都使用自己的错误类型,如果我们使用自己的错误类型,我们应该将这些错误转换为错误类型。我们可以使用std::convert::From标准化特征进行这些转换。// traits inside Rust standard library core convert module/ std::convertpub trait From<T>: Sized { fn from(_: T) -> Self;}????如您所知,String::from()函数用于创建String from &str数据类型。实际上这也是std::convert::From特质的实现。让我们看看如何在自定义错误类型上实现std::convert::From特征。use std::fs::File;use std::io;#[derive(Debug)]struct AppError { kind: String, // type of the error message: String, // error message}// Implement std::convert::From for AppError; from io::Errorimpl From<io::Error> for AppError { fn from(error: io::Error) -> Self { AppError { kind: String::from(“io”), message: error.to_string(), } }}fn main() -> Result<(), AppError> { let _file = File::open(“nonexistent_file.txt”)?; // This generates an io::Error. But because of return type is Result<(), AppError>, it converts to AppError Ok(())}// ————— Run time error —————Error: AppError { kind: “io”, message: “No such file or directory (os error 2)” }在上面的例子中,File::open(“nonexistent.txt”)?产生std::io::Error。但由于返回类型是Result<(), AppError>,它转换为AppError。因为我们正在从main()函数传播错误,所以它会打印出Err的Debug表示形式。在上面的例子中,我们只处理一种std错误类型std::io::Error。让我们看一些处理多种std错误类型的例子。use std::fs::File;use std::io::{self, Read};use std::num;#[derive(Debug)]struct AppError { kind: String, message: String,}// Implement std::convert::From for AppError; from io::Errorimpl From<io::Error> for AppError { fn from(error: io::Error) -> Self { AppError { kind: String::from(“io”), message: error.to_string(), } }}// Implement std::convert::From for AppError; from num::ParseIntErrorimpl From<num::ParseIntError> for AppError { fn from(error: num::ParseIntError) -> Self { AppError { kind: String::from(“parse”), message: error.to_string(), } }}fn main() -> Result<(), AppError> { let mut file = File::open(“hello_world.txt”)?; // generates an io::Error, if can not open the file and converts to an AppError let mut content = String::new(); file.read_to_string(&mut content)?; // generates an io::Error, if can not read file content and converts to an AppError let _number: usize; _number = content.parse()?; // generates num::ParseIntError, if can not convert file content to usize and converts to an AppError Ok(())}// ————— Few possible run time errors —————// 01. If hello_world.txt is a nonexistent fileError: AppError { kind: “io”, message: “No such file or directory (os error 2)” }// 02. If user doesn’t have relevant permission to access hello_world.txtError: AppError { kind: “io”, message: “Permission denied (os error 13)” }// 03. If hello_world.txt contains non-numeric content. ex Hello, world!Error: AppError { kind: “parse”, message: “invalid digit found in string” }???? 搜索有关实现的内容std::io::ErrorKind,以了解如何进一步组织错误类型。
...