【问题标题】:How to do proper error handling inside a map function? [duplicate]如何在地图函数中进行正确的错误处理? [复制]
【发布时间】:2021-12-02 21:34:30
【问题描述】:

我想读取一个文本文件并将所有行转换为 int 值。 我使用这个代码。 但我在这里真正想念的是一种“好”的错误处理方式。

use std::{
    fs::File,
    io::{prelude::*, BufReader},
    path::Path
};

fn lines_from_file(filename: impl AsRef<Path>) -> Vec<i32> {
    let file = File::open(filename).expect("no such file");
    let buf = BufReader::new(file);
    buf.lines()
        .map(|l| l.expect("Could not parse line"))
        .map(|l:String| l.parse::<i32>().expect("could not parse int"))
        .collect()
}

问题:如何进行正确的错误处理? 这是上面的例子“好的锈代码”吗? 还是我应该使用这样的东西:

fn lines_from_file(filename: impl AsRef<Path>) -> Vec<i32> {
    let file = File::open(filename).expect("no such file");
    let buf = BufReader::new(file);
    buf.lines()
        .map(|l| l.expect("Could not parse line"))
        .map(|l:String| match l.parse::<i32>() {
            Ok(num) => num,
            Err(e) => -1 //Do something here 
        }).collect()
}

【问题讨论】:

  • 如 Rust Lang 书中所列,当错误泛化时使用 expect,当发生此错误的原因可能有多种时,您可以匹配它并在 stderr 上打印错误,或者只是如果这是为程序员设计的,请恐慌
  • 如果您的程序需要有效输入,则您的第一个示例很好,如果数据有问题,则应该无法继续。第二个更宽容,并且将允许您可以通过忽略 -1 或其他东西来解决的错误值。无论哪种方式都可以,但是您应该尝试保持一致。 (由于您对错误的行感到恐慌,我建议您也对错误的解析感到恐慌,但我不知道您的最终目标)
  • “良好”的错误处理完全取决于发生错误时您想要做什么expect() 在某些情况下很好。

标签: rust


【解决方案1】:

您实际上可以收集到Result&lt;T, E&gt;See docs

所以你可以收集到Result&lt;Vec&lt;i32&gt;, MyCustomErrorType&gt;。 当您将迭代器转换为返回 Result&lt;i32, MyCustomErrorType&gt; 的迭代器时,此方法有效。迭代在您映射的第一个 Err 处停止。

这是您的工作代码示例。 我使用thiserror crate 进行错误处理

use std::{
    fs::File,
    io::{prelude::*, BufReader},
    num::ParseIntError,
    path::Path,
};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum LineParseError {
    #[error("Failed to read line")]
    IoError(#[from] std::io::Error),
    #[error("Failed to parse int")]
    FailedToParseInt(#[from] ParseIntError),
}

fn lines_from_file(filename: impl AsRef<Path>) -> Result<Vec<i32>, LineParseError> {
    let file = File::open(filename).expect("no such file");
    let buf = BufReader::new(file);
    buf.lines().map(|l| Ok(l?.parse()?)).collect()
}

通过分解这行代码来对代码如何工作的一些小解释:

buf.lines().map(|l| Ok(l?.parse()?)).collect()
  • Rust 推断我们需要收集到一个Result&lt;Vec&lt;i32&gt;, LineParseError&gt;,因为函数的返回类型是Result&lt;Vec&lt;i32&gt;, LineParseError&gt;
  • 在我们编写l? 的映射方法中,如果l 结果包含Err,则映射方法返回ErrLineParseError::IoError 上的#[from] 属性负责转换李>
  • .parse()? 的工作方式相同:#[from] on LineParseError::FailedToParseInt 负责转换
  • 最后但同样重要的是,当映射成功时,我们的方法必须返回Ok(...),这使得collect 变为Result&lt;Vec&lt;i32&gt;, LineParseError&gt; 成为可能。

【讨论】:

    猜你喜欢
    • 2020-05-31
    • 1970-01-01
    • 2014-09-02
    • 2014-06-29
    • 2021-05-11
    • 1970-01-01
    • 2019-01-17
    • 2019-02-14
    • 1970-01-01
    相关资源
    最近更新 更多