代码之家  ›  专栏  ›  技术社区  ›  felipou

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

  •  1
  • felipou  · 技术社区  · 2 年前

    我从上周开始学习生锈,通过阅读书籍和文章,同时尝试从其他语言转换一些代码。

    我遇到了一种情况,我正试图通过下面的代码来举例说明(这是我试图从另一种语言转换的简化版本):

    #[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个整数(我也可以尝试检查其他字符的一些模式,但我忽略了这一部分)。

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

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

    此外,任何关于代码其他部分的反馈/建议都是非常受欢迎的,我正在努力找到在Rust中做事情的“正确方法”。

    1 回复  |  直到 2 年前
        1
  •  1
  •   cdhowie    2 年前

    实现这一点的惯用方法是定义自己的错误类型并返回它,并使用 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 .

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

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