如何在地图函数内进行正确的错误处理?

Ska*_*ary 3 rust

我想读取一个文本文件并将所有行转换为 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()
}
Run Code Online (Sandbox Code Playgroud)

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

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()
}
Run Code Online (Sandbox Code Playgroud)

Jer*_*eke 12

您实际上可以收集到Result<T, E>. 查看文档

这样你就可以收集到一个Result<Vec<i32>, MyCustomErrorType>. 当您将迭代器转换为返回Result<i32, MyCustomErrorType>. 迭代在Err您第一次映射时停止。

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

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()
}
Run Code Online (Sandbox Code Playgroud)

通过分解这行代码来对代码的工作原理进行一些小解释:

buf.lines().map(|l| Ok(l?.parse()?)).collect()
Run Code Online (Sandbox Code Playgroud)
  • Rust 推断我们需要收集到 aResult<Vec<i32>, LineParseError>因为函数的返回类型是Result<Vec<i32>, LineParseError>
  • 在映射方法中,我们编写l?这使得映射方法返回 anErr如果l结果包含 an Err#[from]属性 onLineParseError::IoError负责转换
  • .parse()?工作方式相同:#[from]onLineParseError::FailedToParseInt负责转换
  • Ok(...)最后但并非最不重要的一点是,当映射成功时,我们的方法必须返回,这使得collectinto 成为Result<Vec<i32>, LineParseError>可能。