Rust 错误处理——捕获多个错误

标签 rust error-handling

我上周开始学习 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))
}

所以基本上我想从给定字符串的特定位置提取 3 个整数(我也可以尝试检查其他字符的某些模式,但我已经忽略了那部分)。

我在想,有没有办法同时“捕获”或验证解析调用的所有三个结果?我不想为每个添加匹配 block ,我只想检查是否有人导致错误,并在这种情况下返回另一个错误。有道理吗?

到目前为止,我能想到的唯一解决方案是创建另一个包含所有解析的函数,并匹配其结果。还有其他方法吗?

此外,非常欢迎对代码的其他部分提出任何反馈/建议,我正在努力寻找在 Rust 中做事的“正确方法”。

最佳答案

完成此操作的惯用方法是定义您自己的错误类型并返回它,带有 From<T>每种错误类型的实现 T这可能发生在你的函数中。 ?接线员会做 .into()转换以匹配您的函数声明返回的错误类型。

这里的盒装错误是多余的;只需声明一个枚举,列出函数可能失败的所有方式。整数解析错误的变体甚至可以捕获捕获的错误。

use std::fmt::{Display, Formatter, Error as FmtError};
use std::error::Error;
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)
        }
    }
}

现在我们只需要更改函数的返回类型并让它返回 ExtractCodeError::InvalidStringSize当长度太短时。没有其他需要更改为 ParseIntError自动转换为 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))
}

作为一个额外的好处,这个函数的调用者将能够比使用盒装的 dyn Error 更容易地检查错误。 .

在更复杂的情况下,例如您希望针对每次可能出现的 ParseIntError 稍微调整错误, 你可以使用 .map_err()在结果上转换错误。例如:

something_that_can_fail.map_err(|e| SomeOtherError::Foo(e))?;

关于Rust 错误处理——捕获多个错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71812362/

相关文章:

ruby-on-rails - 如何在Ruby中设置通用错误/异常处理程序回调?

rust - 如何在程序宏生成的代码中创建卫生标识符?

rust - 带有 eq_any 的子查询无法编译

macros - 如何强制类型在编译时实现特征?

error-handling - VBScript CopyFile报告错误53未找到文件,但已成功复制文件

python - Python native 扩展模块 : Can interpreter fail gracefully? 中的崩溃

rust - 如何为notify-rust建立外部超时

configuration - 您可以使用 Cargo 共享依赖树上的 `cfg!` 吗?

ruby - 提高ArgumentError ruby

error-handling - AST具有固定节点而不是antlr中的错误节点