我正在学习Rust,并尝试解决一些基本的算法问题。在许多情况下,我想从stdin中读取行,对每行进行一些转换,然后返回结果项的向量。我这样做的一种方式是这样的:
// Fully working Rust code
let my_values: Vec<u32> = stdin
.lock()
.lines()
.filter_map(Result::ok)
.map(|line| line.parse::<u32>())
.filter_map(Result::ok)
.map(|x|x*2) // For example
.collect();
这有效,但是当然会默默地忽略可能发生的任何错误。现在我想做的事情大致如下:
// Pseudo-ish code
let my_values: Result<Vec<u32>, X> = stdin
.lock()
.lines() // Can cause std::io::Error
.map(|line| line.parse::<u32>()) // Can cause std::num::ParseIntError
.map(|x| x*2)
.collect();
其中X是我以后可以匹配的某种错误类型。最好是我想一次在一行上执行整个操作,并在将字符串数据解析为int之后立即将其丢弃。
我想我需要创建某种Enum类型来容纳各种可能的错误,可能是这样的:
#[derive(Debug)]
enum InputError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
}
但是,我不太了解如何将所有内容放在一起以使其整洁,并避免必须在所有位置进行显式匹配和转换。此外,是否有某种方法可以自动创建这些枚举错误类型,或者每次执行此操作时都必须明确枚举它们吗?
您走在正确的轨道上。我采用的方法是使用您定义的枚举,然后为您感兴趣的错误类型添加From
的实现。这将使您可以在地图上使用?
运算符来获取所需的行为。
#[derive(Debug)]
enum MyError {
IOError(std::io::Error),
ParseIntError(std::num::ParseIntError),
}
impl From<std::io::Error> for MyError {
fn from(e:std::io::Error) -> MyError {
return MyError::IOError(e)
}
}
impl From<std::num::ParseIntError> for MyError {
fn from(e:std::num::ParseIntError) -> MyError {
return MyError::ParseIntError(e)
}
}
然后您可以按照其中任一方式实现实际的转换
let my_values: Vec<_> = stdin
.lock()
.lines()
.map(|line| -> Result<u32,MyError> { Ok(line?.parse::<u32>()?*2) } )
.collect();
这将为您的每个输入提供一个条目,例如:{Ok(x), Err(MyError(x)), Ok(x)}
。或者你可以做:
let my_values: Result<Vec<_>,MyError> = stdin
.lock()
.lines()
.map(|line| -> Result<u32,MyError> { Ok(line?.parse::<u32>()?*2) } )
.collect();
哪个会给您Err(MyError(...))
或Ok([1,2,3])
请注意,您可以通过使用诸如snafu
的错误处理板条箱来进一步减少一些错误样板,但在这种情况下,它不会太多。