fel*_*pou 7 error-handling rust
我上周开始学习 Rust,通过阅读书籍和文章,同时尝试从其他语言转换一些代码。
我遇到了一种情况,我试图通过下面的代码来举例说明(这是我试图从另一种语言转换的简化版本):
#[derive(Debug)]
struct InvalidStringSize;
impl std::fmt::Display for InvalidStringSize {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "string is too short")
}
}
impl std::error::Error for InvalidStringSize {}
pub fn extract_codes_as_ints(
message: String,
) -> Result<(i32, i32, i32), Box<dyn std::error::Error>> {
if message.len() < 20 {
return Err(Box::new(InvalidStringSize {}));
}
let code1: i32 = message[0..3].trim().parse()?;
let code2: i32 = message[9..14].trim().parse()?;
let code3: i32 = message[17..20].trim().parse()?;
Ok((code1, code2, code3))
}
Run Code Online (Sandbox Code Playgroud)
所以基本上我想从给定字符串的特定位置提取 3 个整数(我也可以尝试检查其他字符的某些模式,但我已经忽略了该部分)。
我想知道,有没有办法同时“捕获”或验证解析调用的所有三个结果?我不想为每个添加匹配块,我只想检查是否有人导致错误,并在这种情况下返回另一个错误。说得通?
到目前为止,我能想到的唯一解决方案是创建另一个具有所有解析的函数,并匹配其结果。还有其他方法可以做到这一点吗?
另外,非常欢迎对代码其他部分的任何反馈/建议,我正在努力寻找在 Rust 中做事的“正确方法”。
实现此目的的惯用方法是定义您自己的错误类型并返回它,并From<T>为您的函数中可能出现的每种错误类型提供一个实现T。该?运算符将使用From转换来匹配函数声明返回的错误类型。
盒装错误在这里是多余的;只需声明一个枚举,列出该函数可能失败的所有方式。整数解析错误的变体甚至可以捕获捕获的错误。
use std::error::Error;
use std::fmt::{Display, Formatter, Error as FmtError};
use std::num::ParseIntError;
#[derive(Debug, Clone)]
pub enum ExtractCodeError {
InvalidStringSize,
InvalidInteger(ParseIntError),
}
impl From<ParseIntError> for ExtractCodeError {
fn from(e: ParseIntError) -> Self {
Self::InvalidInteger(e)
}
}
impl Error for ExtractCodeError {}
impl Display for ExtractCodeError {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match self {
Self::InvalidStringSize => write!(f, "string is too short"),
Self::InvalidInteger(e) => write!(f, "invalid integer: {}", e)
}
}
}
Run Code Online (Sandbox Code Playgroud)
另外:如果您使用thiserror板条箱来派生Error,则此代码会变得更简单:
use std::num::ParseIntError;
#[derive(Debug, Clone, thiserror::Error)]
pub enum ExtractCodeError {
#[error("string is too short")]
InvalidStringSize,
#[error("invalid integer: {0}")]
InvalidInteger(#[from] ParseIntError),
}
Run Code Online (Sandbox Code Playgroud)
现在我们只需要更改函数的返回类型并让它ExtractCodeError::InvalidStringSize在长度太短时返回。不需要更改任何其他内容,因为 aParseIntError会自动转换为 an ExtractCodeError:
pub fn extract_codes_as_ints(
message: String,
) -> Result<(i32, i32, i32), ExtractCodeError> {
if message.len() < 20 {
return Err(ExtractCodeError::InvalidStringSize);
}
let code1: i32 = message[0..3].trim().parse()?;
let code2: i32 = message[9..14].trim().parse()?;
let code3: i32 = message[17..20].trim().parse()?;
Ok((code1, code2, code3))
}
Run Code Online (Sandbox Code Playgroud)
作为一个额外的好处,该函数的调用者将能够比使用盒装dyn Error.
在更复杂的情况下,例如您希望针对每次可能出现的 a 稍微调整错误ParseIntError,您可以使用.map_err()结果来转换错误。例如:
something_that_can_fail.map_err(|e| SomeOtherError::Foo(e))?;
Run Code Online (Sandbox Code Playgroud)