我如何在 Rust 中简化这个可怕的嵌套错误处理代码?

How can I simplify this horribly nested error handling code in Rust?

我正在编写一个函数来计算 Rust 中 Vec<Vec<isize>>Summed Area Table,而不使用任何外部板条箱。我正在努力学习尽可能地道地做到这一点,但我 运行 遇到了一些错误处理的障碍。

基本上我要做的就是这个,在维基百科页面上提到:

The summed-area table can be computed efficiently in a single pass over the image, as the value in the summed-area table at (x, y) is just:

where i provides values from the grid, and I from the previously computed values of the table. Obviously, if x or y is 0, then some of these won't exist, in which case they are replaced with 0.

然而,值得注意的是,如果 I(x, y - 1) 不存在,即使 y - 1 存在,那么我们正在使用的网格实际上是 non-rectangular,我们在那种情况下想要 return 一个 NonRectError

有了所有这些背景,这里是代码:我需要防止减法溢出错误,return 特殊情况下的 NonRectError

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 本身的定义,但我不确定从哪个角度来简化它 - 天哪,边缘情况太多了!

是否有任何 built-in 方法可以让我转义这种嵌套的 error-checking 内容?我可以用比这更简单的方式 return NonRectError 吗?

您可以尝试以下操作:

  1. 使用数组,而不是嵌套 Vecs。使用数组,可以保证所有行的宽度都一样,不会出现NonRectError。 (但也许你有充分的理由使用嵌套 Vecs,所以我的其余示例使用嵌套 Vecs。)

  2. 您计算 summed_value 的块很长。我会这样分解它:

    // 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;
    
  3. 而不是检查减法,检查 xiyi 是否为非零更容易。此外,.ok_or() 是将 None 转换为错误的好方法。

    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 链来写。它们都是惯用的,这只是一个偏好问题。我比较喜欢match,因为感觉更简洁

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