在最近的code review过程中,发现自己针对RustResult<T,E>
处理过程中使用的map
,and_then
,map_err
这些组合子理解不是很到位,今天经过查看API文档和运行demo程序,再次对这方面的知识进行学习。
在Rust程序的编写过程中,会存在大量返回Option、Result这类的结果,若是使用match
方式来处理会发现处理过程比较繁琐,需要针对不同的情况分别做处理,特别是在经过多层嵌套后会造成代码的可读性降低。为了简化这方面的问题我们可以使用map
,and_then
等这些组合子来使得我们的程序更具有可读性。关于错误处理的更多内容,可以参考文章。下面分别针对map
,and_then
,map_err
来进行介绍
使用方式介绍
通过在Rust API文档中搜索map这个关键词,我们会发现在很多地方都存在这个方法。这里我们查看Enum std::result::Result
这个枚举里面的定义。通过文档我们知道map
,and_then
,map_err
这类方法可以不用先将Result类型的值取出来,可以直接调用。
1 | pub fn map<U, F>(self, op: F) -> Result<U, E> |
根据map
函数的签名,当Result<T,E>类型实例返回Ok
结果时,使用map
经过F作用后仅返回U类型,即将类型T转换为类型U。这里没有对U类型做任何说明,则表示U可以是任意类型,可以是普通类型,也可以是Result、Option这类更复杂的类型。假如在执行F
这个闭包处理过程中出现Result类型结果,此时的U即为 Result类型,若是不继续处理这个结果, 最后的返回值为Result<Result<_,E>>
;
1 | pub fn and_then<U, F>(self, op: F) -> Result<U, E> |
根据and_then
函数的签名,知道使用and_then
经过F
作用后得到的结果为Result<U, E>
,因此可以用来处理map
得到Result<Result<_,E>>
这种类型的结果(实现少一层result的效果),也可以直接来处理针对F要返回Result
的情况。通过上述定义我们知道,当在表达式中连续使用map调用不会改变最初函数调用可能返回的Err类型,但是在使用and_then时会返回E,此时就会存在当经过多次and_then调用后,返回的Error类型不一样,造成代码编译不能通过。因此在and_then的使用过程中需要将每次调用可能产生的的Err类型转换为一致的Err类型;关于如何返回一致的错误类型,可以查看rust关于自定义错误类型的相关内容(后续补充这部分的文档)。
1 | pub fn map_err<F, O>(self, op: O) -> Result<T, F> |
通过Api文档我们知道map_err
只是用来处理Err这种情况的。其中需要注意的是当map
,and_then
这些组合子在一个表达式中多次连续使用时,产生的Err可能存在多个来源,但是我们在使用map_err
时只会调用一次,并且map_err
会匹配到对应的错误分支。为了更好的理解这部分的错误处理,可以参考以下代码示例。
代码片段
1 | fn chain_match(num: &str) -> Result<u32, String> { |
运行上述代码 我们得到以下结果
1 | test_num1_str is:20 |
通过上述结果我们可以发现map_err
处理的是第一次类型转换中产生的错误。针对后续两个and_then
错误的处理,可以分别将test_num1_str
、test_num1_str
修改为类型转换会失败的情况,通过观察结果就可以发现map_err
的处理细节。
总结
在上述的内容介绍中主要是围绕Result
来进行的,针对Option
他们的处理过程是类似的就没有分别进行叙述。针对Result
、Option
中还有很多类似的组合子,他们实现的效果是类似的,通过查看API文档能够了解他们适用的场景。通过这篇文章的总结,算是对组合子的使用有了更深的理解,能够方便自己以后在复习这方面知识时知道自己的推导过程。