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

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

问题描述

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

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, 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的接口并处理base函数级别的错误:

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)
}

如何在没有附加功能的情况下实现这一点?

How can I achieve this without an additional function?

推荐答案

这是一个问题示例,当您在函数式编程中使用诸如 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) ->选项.当您使用 map 将该 f 应用到 x 时,您将得到嵌套的 Option>代码>,这与您遇到的问题相同.

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, FnOnce(T)->U) ->A<U> 其中 A 类似于包装器类型,例如 OptionResult.在 FP 中,这种类型称为函子.但是它有一个高级版本,称为 monad.除了 map 函数之外,它的接口中还有一个类似的函数,传统上称为 bind,带有类似 (A<T>, FnOnce(T) ->A<U>)->A.更多详情此处.

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 的 OptionResult 不仅是一个函子,也是一个 monad.在我们的例子中,bind 实现为 and_then 方法.例如,您可以在我们的示例中像这样使用它:x.and_then(f),并得到简单的 Option 结果.因此,您可以使用 .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天全站免登陆