Option<T>的map_err方法

5

所以 Result<T, E> 有一个相当不错的方法 map_err,可以以函数式的方式处理错误:

use std::io::Result;
use std::error::Error;
use std::string::ToString;
use std::io;

fn init() -> Result<u32> { Ok(42) }

fn do_work(_data: u32) -> Result<()> { Err(io::Error::new(io::ErrorKind::Other, "IO Error!")) }

fn handle_error<E: Error + ToString>(error: E, message: &str) -> E {
    eprintln!("{}: {}", message, error.to_string());
    error
}

fn main() {
    let _ = init()
        .map_err(|e| handle_error(e, "Init error"))
        .and_then(do_work)
        .map_err(|e| handle_error(e, "Work error")); // "Work error: IO error"
}

如果能够使用相同的函数式风格处理 Option<T>::None,那将很酷。

use std::io::Result;
use std::error::Error;
use std::string::ToString;
use std::io;

fn init_opt() -> Option<u32> { Some(42) }

fn do_work_opt(_data: u32) -> Option<()> { None }

fn handle_none(message: &str) {
    eprintln!("{}", message);
}

fn main() {
    let _ = init_opt()
        .map_none(|| handle_none("Init error"))
        .and_then(do_work_opt)
        .map_none(|| handle_none("Work error")); // "Work error"
}

但我在 Option文档 中并没有看到任何适合替代这种方法的内容。

可以使用自定义 trait 来实现该功能,如下所示

trait MapNone {
    fn map_none(self, op: impl FnOnce() -> ()) -> Self;
}

impl<T> MapNone for Option<T> {
    fn map_none(self, op: impl FnOnce() -> ()) -> Self {
        if self.is_none() { op(); } 
        self
    }
}

但我相信我错过了某些东西,而且通过标准库也有一种漂亮的方式来实现同样的功能。

完整代码示例


1
在函数式编程中,返回单位类型的函数并不是非常有用,因为它需要通过副作用来操作。Optionor_elseunwrap_or_else 方法,可以更加有用地处理 None - user4815162342
我并不是指没有副作用的精确函数惯用法,而是一种链式处理数据的方式。 - Alexey Larionov
仅使用stdlib的.map_none(|| {….})可以表示为or_else(|| {...; None})。我不确定这是否回答了你的问题,即它是否足够简洁明了。 - user4815162342
它的回答让我知道,没有比我已经考虑过的更方便的方法。谢谢。 - Alexey Larionov
请@user4815162342回答,如果您想被接受。 - Alexey Larionov
谢谢,我很满意同时发布的答案被接受了。 - user4815162342
1个回答

6

存在一个名为Option::or_else的函数,它是您所提出的方法的一般化形式。与要求其函数返回单元类型不同,它返回一个Option,因此只需让它返回None以模拟您的用例。

fn main() {
    let _ = init_opt()
        .or_else(|| {handle_none("Init error"); None})
        .and_then(do_work_opt)
        .or_else(|| {handle_none("Work error"); None}); // "Work error"
}

当然,现在也可以返回Some(x)以便不同分支进行分支操作。

最后,or_elseand_then也可以与map_or_else结合使用:

fn main() {
    let _ = init_opt()
      .map_or_else(|| {handle_none("Init error"); None}, do_work_opt)
      .or_else(|| {handle_none("Work error"); None}); // "Work error"
}

这段代码应该和上面的代码做相同的事情; map_or_else 根据选项是 None 还是包含一个值来分支。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接