Rust嵌套迭代器中return无法从外层函数返回的问题求解
问题解决:生成数独网格时返回位置错误信息
问题背景
说明:
- 最初的问题表述不清。我想要的是能直接返回错误的方案,但
std::array::try_from_fn并不支持,当时我对此并不了解。 - 在等待
std::array::try_from_fn稳定的过程中,有很多关于替代方案和临时解决办法的优质讨论(这些方案可能会有性能损耗,但编译器大概率会优化掉)。
我编写了一个从9×9数字网格生成数独网格的函数unsolved_sudoku_from_nums。当检测到无效数字时,函数应返回该数字所在位置的信息(相关功能在注释代码中)。当前代码可以运行,但如果用注释代码替换.unwrap(),编译器会抛出一系列错误。经分析,这是因为return语句是向std::array::from_fn返回值,而非外层的unsolved_sudoku_from_nums函数。请问如何修改才能实现预期功能?谢谢。
原代码:
/// parse nums into an UnsovedSudoku, 1-9 will be parsed as values 1-9, 0 will be parsed as an unsolved cell and all /// other values will cause the function to return an InvalidInt error. fn unsolved_sudoku_from_nums(nums: &[[u8; 9]; 9]) -> Result<UnsolvedSudoku, InvalidInt> { Ok(UnsolvedSudoku ( std::array::from_fn(|i|{ std::array::from_fn(|j| { match nums[i][j] { 0 => WorkingCell::PossibleValues([true; 9]), int => WorkingCell::Solved( SudokuValue::from_int(int) .unwrap() // .expect( // return Err(InvalidInt{ // row: i, // column: j, // int: nums[i][j], // }) // ) ), } }) }) )) }
补充定义:
struct UnsolvedSudoku ( [[WorkingCell; 9]; 9] );
解决方案
方案1:手动嵌套遍历(稳定版Rust推荐)
由于std::array::from_fn不支持在闭包中向外层函数传播Result错误,最直接的方式是手动遍历9×9数组,逐个处理元素,遇到无效数字时直接返回带位置信息的错误:
fn unsolved_sudoku_from_nums(nums: &[[u8; 9]; 9]) -> Result<UnsolvedSudoku, InvalidInt> { // 初始化默认网格,所有单元格为待求解状态 let mut grid = [[WorkingCell::PossibleValues([true; 9]); 9]; 9]; for i in 0..9 { for j in 0..9 { let int = nums[i][j]; grid[i][j] = match int { 0 => WorkingCell::PossibleValues([true; 9]), _ => { // 尝试转换数字,失败则返回带位置的错误 let value = SudokuValue::from_int(int) .ok_or_else(|| InvalidInt { row: i, column: j, int, })?; WorkingCell::Solved(value) } }; } } Ok(UnsolvedSudoku(grid)) }
这种方法逻辑直观,可读性强,对于固定大小的9×9数组,编译器会完全优化循环开销,不会有性能损失。
方案2:利用迭代器与collect(函数式风格)
Rust的数组支持通过Result迭代器collect成Result<[T; N], E>,当迭代器中任何一个元素返回Err时,整个collect会立即终止并返回该错误:
fn unsolved_sudoku_from_nums(nums: &[[u8; 9]; 9]) -> Result<UnsolvedSudoku, InvalidInt> { let grid: Result<[[WorkingCell; 9]; 9], _> = nums .iter() .enumerate() .map(|(i, row)| { row.iter() .enumerate() .map(|(j, &int)| match int { 0 => Ok(WorkingCell::PossibleValues([true; 9])), _ => SudokuValue::from_int(int) .map(WorkingCell::Solved) .ok_or_else(|| InvalidInt { row: i, column: j, int }), }) // 将一行的Result迭代器收集为Result<[WorkingCell;9], InvalidInt> .collect::<Result<[WorkingCell; 9], _>>() }) // 将所有行的Result收集为Result<[[WorkingCell;9];9], InvalidInt> .collect(); Ok(UnsolvedSudoku(grid?)) }
这种写法偏向函数式风格,代码更紧凑,同样能实现错误的正确传播。
方案3:使用不稳定的try_from_fn(Nightly Rust)
如果可以使用Nightly版本的Rust,启用array_try_from_fn特性后,std::array::try_from_fn支持在闭包中返回Result并自动处理错误传播:
#![feature(array_try_from_fn)] fn unsolved_sudoku_from_nums(nums: &[[u8; 9]; 9]) -> Result<UnsolvedSudoku, InvalidInt> { Ok(UnsolvedSudoku( std::array::try_from_fn(|i| { std::array::try_from_fn(|j| { match nums[i][j] { 0 => Ok(WorkingCell::PossibleValues([true; 9])), int => SudokuValue::from_int(int) .map(WorkingCell::Solved) .ok_or_else(|| InvalidInt { row: i, column: j, int }), } }) })? )) }
注意该特性目前尚未稳定,不适合用于生产环境的稳定版代码。
内容的提问来源于stack exchange,提问作者Pioneer_11
相关产品推荐
相关产品推荐

