内容简介:上一篇文章,从宏观的角度介绍了Rust错误处理的方法。提到了Option,作为弱化版的Result。Option作为Rust的的系统类型,用来表示值不存在的可能。在编程中,这是一个非常好的做法,因为它强制Rust检测和处理值不存在的情况。Option和Result,Rust都提供了一些组合算子来,来简化代码的书写,提供更强大的表达力。
前言
上一篇文章,从宏观的角度介绍了Rust错误处理的方法。提到了Option,作为弱化版的Result。
enum Option<T>{ None, Some(T), }
Option作为Rust的的系统类型,用来表示值不存在的可能。在编程中,这是一个非常好的做法,因为它强制Rust检测和处理值不存在的情况。
Option和Result,Rust都提供了一些组合算子来,来简化代码的书写,提供更强大的表达力。
Option相关的组合算子
ok_or和ok_or_else
这两个是一组,作用都是从Option转成Result。
pub fn ok_or<E>(self, err: E) -> Result<T, E> pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E> where F: FnOnce() -> E,
其作用细细来讲,如下:
- Some(v) –> Ok(v)
- None –> Err(err)
其行为大概如下所示:
fn ok_or<T, E>(option: Option<T>, err: E) -> Result<T, E> { match option { Some(val) => Ok(val), None => Err(err), } }
下面以一个例子来展示这个组合算子的作用
use std::env; fn double_arg(mut argv: env::Args) -> Result<i32, String> { argv.nth(1) .ok_or("Please give at least one argument".to_owned()) .and_then(|arg| arg.parse::<i32>().map_err(|err| err.to_string())) } fn main() { match double_arg(env::args()) { Ok(n) => println!("{}", n), Err(err) => println!("Error: {}", err), } }
如果执行,不给任何参数:
Error: Please give at least one argument
给一个参数为5,输出如下:
nth(1) 因为没有第一个参数(0-index),所以Option的值为None,ok_or将Option转成了Result,最终返回的Err(err)。
map
这个组合算子的作用是将Option
- Some(T) ->Some(U)
- None -> None
fn main() { let maybe_some_string = Some(String::from("Hello, World!")); let maybe_some_len = maybe_some_string.map(|s| s.len()); assert_eq!(maybe_some_len, Some(13)); }
注意map是值传递,会消耗掉原始的值。
and_then
and_then 又被称为flatmap,它的作用是从一个Option转成另一Option:
- None -> None
- Some(x) -> Some(f(x))
对于Some而言,and_then把包裹的值作为参数
fn sq(x: u32) -> Option<u32> { Some(x * x) } fn nope(_: u32) -> Option<u32> { None } assert_eq!(Some(2).and_then(sq).and_then(sq), Some(16)); assert_eq!(Some(2).and_then(sq).and_then(nope), None); assert_eq!(Some(2).and_then(nope).and_then(sq), None); assert_eq!(None.and_then(sq).and_then(sq), None);
注意sq函数的入参,是u32型,并不是Some()。
Result相关的组合算子
is_ok and is_err
result.is_ok和result.is_err返回bool型的结果。
对于is_ok而言:
- Ok(T) 返回true
- Err(E) 返回false
对于is_err而言:
- Ok(T)返回false
- Err(E)返回true
let x: Result<i32, &str> = Err("Some error message"); assert_eq!(x.is_err(), true); let x: Result<i32, &str> = Ok(-3); assert_eq!(x.is_ok(), true);
ok and err
pub fn ok(self) -> Option<T> pub fn err(self) -> Option<E>
注意,这两个算子都是将Result转成Option类型,一个关注成功的情况,一个关注出错的情况。
result.ok() 如果Result返回Ok(T),那么返回值是Option
let x: Result<u32, &str> = Ok(2); assert_eq!(x.ok(), Some(2)); let x: Result<u32, &str> = Err("Nothing here"); assert_eq!(x.ok(), None);
result.err()如果Result失败,返回Err(E),那么返回返回Option
let x: Result<u32, &str> = Ok(2); assert_eq!(x.err(), None); let x: Result<u32, &str> = Err("Nothing here"); assert_eq!(x.err(), Some("Nothing here"));
and_then
Option也有and_then 算子,Result和Option一样的,语义是一样的。而且有很多这样的算子。
use std::env; fn double_arg(mut argv: env::Args) -> Result<i32, String> { argv.nth(1) .ok_or("Please give at least one argument".to_owned()) .and_then(|arg| arg.parse::<i32>().map_err(|err| err.to_string())) } fn main() { match double_arg(env::args()) { Ok(n) => println!("{}", n), Err(err) => println!("Error: {}", err), } }
还是前面的例子, ok_or()之后,Option就转成了Result,如果Ok的情况下,类型是字符串,那么and_then 会尝试将字符串解析成i32。
pub fn and_then<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> Result<U, E>,
如果Result的值是Ok(T),那么执行op(T),否则直接返回Err。
n sq(x: u32) -> Result<u32, u32> { Ok(x * x) } fn err(x: u32) -> Result<u32, u32> { Err(x) } assert_eq!(Ok(2).and_then(sq).and_then(sq), Ok(16)); assert_eq!(Ok(2).and_then(sq).and_then(err), Err(4)); assert_eq!(Ok(2).and_then(err).and_then(sq), Err(2)); assert_eq!(Err(3).and_then(sq).and_then(sq), Err(3));
map and map_err
上面的例子中给出了map_err的使用。map和map_err是对Result中的Ok(T)或者Err(E)执行某个函数或者执行某种变换,得到新的Result:
pub fn map<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> U, pub fn map_err<F, O>(self, op: O) -> Result<T, F> where O: FnOnce(E) -> F,
对于map函数而言:
- 如果Result值是Ok(T),对T执行函数
- 如果Result的值是Err(E),保持不变,新的结果Option的值也是Err(E)
如下实例代码:
let line = "1\n2\n3\n4\n"; for num in line.lines() { match num.parse::<i32>().map(|i| i * 2) { Ok(n) => println!("{}", n), Err(..) => {} } }
对于map_err而言:
- 如果Result的值是Err(E),对E执行函数
- 如果Result的值是Ok(T),保持不变。结果Result和输入Result一致。
fn stringify(x: u32) -> String { format!("error code: {}", x) } let x: Result<u32, u32> = Ok(2); assert_eq!(x.map_err(stringify), Ok(2)); let x: Result<u32, u32> = Err(13); assert_eq!(x.map_err(stringify), Err("error code: 13".to_string()));
map_err可以用来对错误进行转换。
代码赏析
最后的最后,赏析一段Error Handle的代码
use std::fs::File; use std::io::Read; use std::path::Path; fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> { File::open(file_path) .map_err(|err| err.to_string()) .and_then(|mut file| { let mut contents = String::new(); file.read_to_string(&mut contents) .map_err(|err| err.to_string()) .map(|_| contents) }) .and_then(|contents| { contents.trim().parse::<i32>() .map_err(|err| err.to_string()) }) .map(|n| 2 * n) } fn main() { match file_double("foobar") { Ok(n) => println!("{}", n), Err(err) => println!("Error: {}", err), } }
上面这段代码中,有很多种不同的错误类型:
- io::Error
- num::ParseIntError
通过map_err都转成了Result<i32,String>类型。
上述写法看起来技巧性很强,但是我不喜欢,属于炫技派做法,心智负担比较重。
use std::fs::File; use std::io::Read; use std::path::Path; fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> { let mut file = match File::open(file_path) { Ok(file) => file, Err(err) => return Err(err.to_string()), }; let mut contents = String::new(); if let Err(err) = file.read_to_string(&mut contents) { return Err(err.to_string()); } let n: i32 = match contents.trim().parse() { Ok(n) => n, Err(err) => return Err(err.to_string()), }; Ok(2 * n) } fn main() { match file_double("foobar") { Ok(n) => println!("{}", n), Err(err) => println!("Error: {}", err), } }
这段代码,看起来更舒服,更易懂。
以上所述就是小编给大家介绍的《Option和Result相关的组合算子》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Python 图像处理 OpenCV (13): Scharr 算子和 LOG 算子边缘检测技术
- Spark 算子讲解(action 篇)
- Apache Flink 漫谈系列 - JOIN 算子
- Spark 系列(四)—— RDD常用算子详解
- 边缘检测原理 - Sobel, Laplace, Canny算子
- Spark 性能调优:RDD 算子调优篇
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。