如何展平嵌套结果? [英] How can I flatten nested Results?

查看:89
本文介绍了如何展平嵌套结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用第三方库,该库提供了我必须按原样使用的基于树的数据结构。 API返回 Result< T,Error> 。我必须进行一些顺序调用,并将错误转换为应用程序的内部错误。

I'm working with a third-party library that provides tree-based data structures that I have to use "as is". The API returns Result<T, Error>. I have to make some sequential calls and convert the error to my application's internal error.

use std::error::Error;
use std::fmt;

pub struct Tree {
    branches: Vec<Tree>,
}

impl Tree {
    pub fn new(branches: Vec<Tree>) -> Self {
        Tree { branches }
    }

    pub fn get_branch(&self, id: usize) -> Result<&Tree, TreeError> {
        self.branches.get(id).ok_or(TreeError {
            description: "not found".to_string(),
        })
    }
}

#[derive(Debug)]
pub struct TreeError {
    description: String,
}

impl Error for TreeError {
    fn description(&self) -> &str {
        self.description.as_str()
    }
}

impl fmt::Display for TreeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description.fmt(f)
    }
}

#[derive(Debug)]
pub struct MyAwesomeError {
    description: String,
}

impl MyAwesomeError {
    pub fn from<T: fmt::Debug>(t: T) -> Self {
        MyAwesomeError {
            description: format!("{:?}", t),
        }
    }
}

impl Error for MyAwesomeError {
    fn description(&self) -> &str {
        &self.description
    }
}

impl fmt::Display for MyAwesomeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description.fmt(f)
    }
}

如果我编写这段代码:

pub fn take_first_three_times(tree: &Tree) -> Result<&Tree, MyAwesomeError> {
    let result = tree
        .get_branch(0)
        .map(|r| r.get_branch(0))
        .map(|r| r.map(|r| r.get_branch(0)));
    //    ...
}

结果类型将是 Result< Result< Result< Tree,TreeError> ;, TreeError> ;, TreeError> 。我不想通过 match 级联来处理错误。

The type of result will be Result<Result<Result<Tree, TreeError>, TreeError>, TreeError>. I don't want to process errors by cascades of match.

我可以编写一个内部函数来调整API的接口和过程基本功能级别的错误:

I can write an internal function that adjusts the API's interface and processes the error in the level of base function:

fn take_first_three_times_internal(tree: &Tree) -> Result<&Tree, TreeError> {
    tree.get_branch(0)?.get_branch(0)?.get_branch(0)
}

pub fn take_first_three_times(tree: &Tree) -> Result<&Tree, MyAwesomeError> {
    take_first_three_times_internal(tree).map_err(MyAwesomeError::from)
}

我该如何

推荐答案

这是一个示例,当您使用诸如 Option 在函数式编程中。在函数式编程中,有所谓的纯函数,它们不更改某些状态(全局变量,输出参数)而仅依赖于输入参数,并且仅将其结果作为返回值返回而没有任何副作用。它使程序更具可预测性和安全性,但带来了一些不便。

This is an example of issue, when you're working with various wrappers like Option in functional programming. In functional programming there are such called 'pure' functions, that instead of changing some state (global variables, out parameters) only rely on input parameters and only return their result as return value without any side effects. It makes programs much more predictable and safe, but it introduced some inconveniences.

想象一下,我们有 let x = Some(2)和某些函数 f(x:i32)->选项< f32> 。当您使用 map 将该 f 应用于 x 时,您会嵌套 Option< Option< f32>> ,这与您遇到的问题相同。

Imagine we have let x = Some(2) and some function f(x: i32) -> Option<f32>. When you use map to apply that f to x, you'll get nested Option<Option<f32>>, which is the same issue that you got.

但是在函数式程序设计领域(Rust的灵感源自他们的想法,并支持许多典型的函数式功能),他们提出了解决方案:monads。

But in the world of functional programming (Rust was inspired with their ideas a lot and supports a lot of typical 'functional' features) they came up with solution: monads.

我们可以显示 map 签名,例如(A< T,FnOnce(T)-> U)-> A< U> ,其中 A 类似于包装器类型,例如 Option 结果。在FP中,此类类型称为函子。但是它有一个高级版本,称为monad。除了 map 函数外,它在其界面中还有一个类似的函数,通常称为 bind ,其签名如(A T,FnOnce(T)-> A U)--。 A< U> 。更多详细信息那里

We can show map a signature like (A<T>, FnOnce(T)->U) -> A<U> where A is something like wrapper type, such as Option or Result. In FP such types are called functors. But there is an advanced version of it, called a monad. It has, besides the map function, one more similar function in its interface, traditionally called bind with signature like (A<T>, FnOnce(T) -> A<U>) -> A<U>. More details there.

在实际上,Rust的 Option Result 不仅是函子,而且还是单子。在我们的案例中,该 bind 被实现为 and_then 方法。例如,您可以在我们的示例中使用它,例如: x.and_then(f),并获得简单的 Option< f32> 结果。因此,可以使用 .and_then 链代替 .map 链,该链的作用非常相似,但不会嵌套结果。

In fact, Rust's Option and Result is not only a functor, but a monad too. That bind in our case is implemented as and_then method. For example, you could use it in our example like this: x.and_then(f), and get simple Option<f32> as a result. So instead of a .map chain you could have .and_then chain that will act very similar, but there will be no nested results.

这篇关于如何展平嵌套结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆