error-handling - 如何展平嵌套结果?

标签 error-handling rust flatten

我正在使用第三方库,该库提供我必须“按原样”使用的基于树的数据结构。 API 返回 Result<T, 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<Result<Tree, TreeError>, TreeError>, TreeError> .我不想通过级联处理错误 match .

我可以编写一个内部函数来调整 API 的接口(interface)并处理基本函数级别的错误:

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在函数式编程中。在函数式编程中有所谓的“纯”函数,而不是改变一些状态(全局变量,输出参数)只依赖于输入参数并且只返回它们的结果作为返回值而没有任何副作用。它使程序更具可预测性和安全性,但也带来了一些不便。

假设我们有 let x = Some(2)和一些功能 f(x: i32) -> Option<f32> .当您使用 map应用 fx , 你会嵌套 Option<Option<f32>> ,这与您遇到的问题相同。

但是在函数式编程的世界里(Rust 从他们的想法中获得了很多灵感并支持许多典型的“函数式”特性)他们提出了解决方案:monads。

我们可以显示map(A<T>, FnOnce(T)->U) -> A<U>这样的签名其中 A类似于包装类型,例如 OptionResult .在 FP 中,此类类型称为仿函数。但是它有一个高级版本,称为 monad。它有,除了map功能,其界面中还有一个类似的功能,传统上称为 bind签名如 (A<T>, FnOnce(T) -> A<U>) -> A<U> .更多详情 there .

事实上,Rust 的 OptionResult不仅是仿函数,还是单子(monad)。那bind在我们的例子中实现为 and_then 方法。例如,您可以像这样在我们的示例中使用它:x.and_then(f) ,然后变得简单 Option<f32>因此。所以不是 .map你可以拥有链.and_then行为非常相似的链,但不会有嵌套结果。

关于error-handling - 如何展平嵌套结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59028327/

相关文章:

rust - 无法编译 Rust 构建器模式,因为借用的值生命周期不够长

rust - Rust 中的组合运算符和管道转发运算符

JavaScript 扁平化对象数组的数组

javascript - 如何仅将多维数组的一部分展平一个级别?

error-handling - 有没有办法在规则 catch[...] block 中处理所有 antlr 异常类型

ios - IOS App上的错误处理?

c# - 引发登录/注册错误的异常是否合适?

error-handling - 消除SAS中缺少值的log10错误

python - 如何展平嵌套的字典并在碰撞时使用内部值

rust - 最适合分布式环境的 Rust 时间类型是什么?