繁体   English   中英

如何转换结果<T, E1>导致<T, E2>问号运算符何时无效?

[英]How can I convert a Result<T, E1> to Result<T, E2> when the question mark operator is ineffective?

E2E1实现了From特征时Result<T, E2>是否有一种惯用/简洁的方法将Result<T, E1>转换为Result<T, E2>

我不能使用? 运算符,因为它不编译。

就我而言, E1ParseIntErrorE2是自定义CalcPackageSizeError错误枚举。

操场

use std::error;
use std::fmt;
use std::io;
use std::io::Read;
use std::num::ParseIntError;

#[derive(Debug)]
enum CalcPackageSizeError {
    InvalidInput(&'static str),
    BadNum(&'static str),
}
impl error::Error for CalcPackageSizeError {}
impl fmt::Display for CalcPackageSizeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            match *self {
                Self::InvalidInput(err_desc) => err_desc,
                Self::BadNum(err_desc) => err_desc,
            }
        )
    }
}

impl From<ParseIntError> for CalcPackageSizeError {
    fn from(_: ParseIntError) -> Self {
        CalcPackageSizeError::BadNum(
            "Error in calculating size of one or more of the packages involved.",
        )
    }
}

fn parse_comma_separated_num(num_str: &str) -> Result<usize, ParseIntError> {
    num_str
        .chars()
        .filter(|char| *char != ',')
        .collect::<String>()
        .parse::<usize>()
}

fn calc_all_package_size(contents: &str) -> Result<usize, CalcPackageSizeError> {
    contents
        .split('\n')
        .skip(2)
        .map(|package_str| {
            let amount_str = package_str
                .split(' ')
                .filter(|element| *element != "")
                .nth(1);

            if let Some(amt_str) = amount_str {
                parse_comma_separated_num(amt_str)?
                // match parse_comma_separated_num(amt_str) {
                //     Ok(amt) => Ok(amt),
                //     Err(err) => Err(From::from(err)),
                // }
            } else {
                Err(CalcPackageSizeError::InvalidInput("Input not as expected, expected the 2nd spaces-delimited item to be the size (integer)."))
            }
        })
        .sum()
}

fn main() {
    let mut wajig_input = String::from(
        "Package                           Size (KB)        Status
=================================-==========-============
geoip-database                      10,015      installed
aptitude-common                     10,099      installed
ieee-data                           10,137      installed
hplip-data                          10,195      installed
librsvg2-2                          10,412      installed
fonts-noto-color-emoji              10,610      installed",
    );
    // io::stdin().read_to_string(&mut wajig_input).expect("stdin io rarely fails.");
    match calc_all_package_size(wajig_input.as_str()) {
        Ok(total_size_in_kb) => {
            let size_in_mb = total_size_in_kb as f64 / 1024.0;
            println!("Total size of packages installed: {} MB", size_in_mb);
        }
        Err(error) => {
            println!("Oops! Encountered some error while calculating packages' size.");
            println!("Here's the error: \n {}", error);
            println!("\n-- Gracefully exiting..");
        }
    }
}

这给出了编译错误:

error[E0308]: `if` and `else` have incompatible types
  --> src/main.rs:59:17
   |
52 | /             if let Some(amt_str) = amount_str {
53 | |                 parse_comma_separated_num(amt_str)?
   | |                 ----------------------------------- expected because of this
54 | |                 // match parse_comma_separated_num(amt_str) {
55 | |                 //     Ok(amt) => Ok(amt),
...  |
59 | |                 Err(CalcPackageSizeError::InvalidInput("Input not as expected, expected the 2nd spaces-delimited item to be the size (integer)."))
   | |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found enum `Result`
60 | |             }
   | |_____________- `if` and `else` have incompatible types
   |
   = note: expected type `usize`
              found enum `Result<_, CalcPackageSizeError>`
note: return type inferred to be `usize` here
  --> src/main.rs:53:17
   |
53 |                 parse_comma_separated_num(amt_str)?
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

虽然这两个错误在语义上看起来很相似,但我需要以不同的方式来应对这两种情况,所以我不能把它们放在一起。

使用Result::map_errInto::into

if let Some(amt_str) = amount_str {
    parse_comma_separated_num(amt_str).map_err(Into::into)
} else {
    Err(CalcPackageSizeError::InvalidInput("Input not as expected, expected the 2nd spaces-delimited item to be the size (integer)."))
}

正如Jmb 在评论中指出的那样,您还可以使用Ok? 一起:

if let Some(amt_str) = amount_str {
    Ok(parse_comma_separated_num(amt_str)?)
} else {
    Err(CalcPackageSizeError::InvalidInput("Input not as expected, expected the 2nd spaces-delimited item to be the size (integer)."))
}

问题是? 成功时解包值,失败时从函数返回。 这意味着parse_comma_separated_num(amt_str)? 计算结果为usize ,正如编译器告诉您的那样:

返回类型推断为usize这里

这将导致第一个块评估为usize ,第二个块评估为Result 这些不是同一类型,导致您得到错误。

使用map_err转换错误类型map_err值保留为Result ,从而允许两个块评估为相同的类型。

也可以看看:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM