Error handling 如何简化Rust中可怕的嵌套错误处理代码?

Error handling 如何简化Rust中可怕的嵌套错误处理代码?,error-handling,rust,refactoring,Error Handling,Rust,Refactoring,我正在写一个函数来计算生锈的Vec的a,而不使用任何外部板条箱。我正在努力学习如何尽可能地惯用这种方式,但在错误处理方面遇到了一些障碍 基本上我想做的就是,维基百科页面上提到: 一次通过就可以有效地计算总面积表 在图像上,由于x,y处的求和面积表中的值为 只是: 其中,我提供了来自网格的值,我提供了来自上一个网格的值 表的计算值。显然,如果x或y为0,那么 这些将不存在,在这种情况下,它们将替换为0 但是,值得注意的是,如果Ix,y-1不存在,即使y-1存在,那么我们使用的网格实际上是非矩形的,

我正在写一个函数来计算生锈的Vec的a,而不使用任何外部板条箱。我正在努力学习如何尽可能地惯用这种方式,但在错误处理方面遇到了一些障碍

基本上我想做的就是,维基百科页面上提到:

一次通过就可以有效地计算总面积表 在图像上,由于x,y处的求和面积表中的值为 只是:

其中,我提供了来自网格的值,我提供了来自上一个网格的值 表的计算值。显然,如果x或y为0,那么 这些将不存在,在这种情况下,它们将替换为0

但是,值得注意的是,如果Ix,y-1不存在,即使y-1存在,那么我们使用的网格实际上是非矩形的,在这种情况下,我们希望返回一个非字符错误

在所有这些背景下,下面是代码:我需要防止减法产生溢出错误,并在特殊情况下返回非字符错误:

fn compute_summed_area_table(grid: &Vec<Vec<isize>>) -> Result<Vec<Vec<isize>>, NonRectError> {
    let mut summed_area_table =
        vec![Vec::with_capacity(grid[0].len()); grid.len()];

    for (yi, row) in grid.iter().enumerate() {
        for (xi, &value) in row.iter().enumerate() {
            let (prev_row, prev_column_idx) = (
                yi.checked_sub(1).and_then(|i| summed_area_table.get(i)),
                xi.checked_sub(1)
            );

            let summed_values =
                value +
                // I(x, y - 1)
                match prev_row {
                    None => &0,
                    Some(prev_row_vec) => match prev_row_vec.get(xi) {
                        Some(v) => v,
                        None => return Err(NonRectError { xi, yi })
                    }
                } +
                // I(x - 1, y)
                (prev_column_idx
                     .and_then(|i| summed_area_table[yi].get(i))
                     .unwrap_or(&0)) -
                // I(x - 1, y - 1)
                (prev_row
                     .map_or(&0, |r| {
                         prev_column_idx
                             .and_then(|i| r.get(i))
                             .unwrap_or(&0)
                     }));

            summed_area_table[yi].push(summed_values);
        }
    }

    Ok(summed_area_table)
}

// Omitted the definition of NonRectError here, but it is defined.
这段代码显然是sin本身的定义,但我不确定从哪个角度来简化它-有这么多该死的边缘案例


是否有任何内置方法可以让我逃避这种嵌套的错误检查?我可以用比这更简单的方法返回非字符错误吗?

以下是一些您可以尝试的方法:

使用数组,而不是嵌套的向量。使用数组,可以保证所有行的宽度相同,并且不会出现非字符错误。但是也许你有充分的理由使用嵌套的vec,所以我的其他示例使用嵌套的vec

计算求和值的块相当长。我会这样分解它:

// I(x, y - 1)
let north = ...;

// I(x - 1, y)
let west = ...;

// I(x - 1, y - 1)
let northwest = ...;

let summed_values = value + north + west - northwest;

而不是检验减法,更容易检查席和彝是否为非零。此外,这也是将“无”转换为错误的好方法

let northwest = match (xi, yi) {
    (0, _) => 0,
    (_, 0) => 0,
    (_, _) => {
        // We know xi and yi are nonzero, so we can subtract without checks
        summed_area_table.get(yi - 1)
            .and_then(|row| row.get(xi - 1))
            .ok_or(NonRectError { xi, yi })?
    }
};
你也可以用if/else链来写。他们都是惯用语,这只是偏好的问题。我更喜欢火柴,因为它感觉更简洁

let northwest = if xi == 0 {
    0
} else if yi == 0 {
    0
} else {
    // same as above
};

以下是一些您可以尝试的东西:

使用数组,而不是嵌套的向量。使用数组,可以保证所有行的宽度相同,并且不会出现非字符错误。但是也许你有充分的理由使用嵌套的vec,所以我的其他示例使用嵌套的vec

计算求和值的块相当长。我会这样分解它:

// I(x, y - 1)
let north = ...;

// I(x - 1, y)
let west = ...;

// I(x - 1, y - 1)
let northwest = ...;

let summed_values = value + north + west - northwest;

而不是检验减法,更容易检查席和彝是否为非零。此外,这也是将“无”转换为错误的好方法

let northwest = match (xi, yi) {
    (0, _) => 0,
    (_, 0) => 0,
    (_, _) => {
        // We know xi and yi are nonzero, so we can subtract without checks
        summed_area_table.get(yi - 1)
            .and_then(|row| row.get(xi - 1))
            .ok_or(NonRectError { xi, yi })?
    }
};
你也可以用if/else链来写。他们都是惯用语,这只是偏好的问题。我更喜欢火柴,因为它感觉更简洁

let northwest = if xi == 0 {
    0
} else if yi == 0 {
    0
} else {
    // same as above
};

一个想法:将一些位抽象为函数?@JoelBerkeley好吧,当然,但这里的大多数实际工作只是一个大的加减运算,它们似乎相互耦合,在当前状态下无法真正分离。一个想法:将一些位抽象为函数?@JoelBerkeley好吧,当然,但这里的大部分实际工作只是一个大的加法和减法,而且它们之间似乎过于耦合,在当前状态下无法真正分离。