当问号运算符无效时,如何将 Result<T, E1> 转换为 Result<T, E2>?
How can I convert a Result<T, E1> to Result<T, E2> when the question mark operator is ineffective?
当 E2
为 E1
实现了 From
特性时,是否有 idiomatic/concise 方法将 Result<T, E1>
转换为 Result<T, E2>
?
我无法使用 ?
运算符,因为它无法编译。
在我的例子中,E1
是 ParseIntError
,E2
是自定义的 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_err
and Into::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)."))
}
作为,你也可以同时使用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)."))
}
问题是 ?
在失败时从函数中解包成功 和 returns 的值。这意味着 parse_comma_separated_num(amt_str)?
的计算结果为 usize
,正如编译器告诉您的那样:
return type inferred to be usize
here
这将导致第一个块的计算结果为 usize
,第二个块的计算结果为 Result
。这些不是同一类型,导致您收到错误。
使用 map_err
转换错误类型会将值保留为 Result
,允许两个块评估为同一类型。
另请参阅:
当 E2
为 E1
实现了 From
特性时,是否有 idiomatic/concise 方法将 Result<T, E1>
转换为 Result<T, E2>
?
我无法使用 ?
运算符,因为它无法编译。
在我的例子中,E1
是 ParseIntError
,E2
是自定义的 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_err
and Into::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)."))
}
作为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)."))
}
问题是 ?
在失败时从函数中解包成功 和 returns 的值。这意味着 parse_comma_separated_num(amt_str)?
的计算结果为 usize
,正如编译器告诉您的那样:
return type inferred to be
usize
here
这将导致第一个块的计算结果为 usize
,第二个块的计算结果为 Result
。这些不是同一类型,导致您收到错误。
使用 map_err
转换错误类型会将值保留为 Result
,允许两个块评估为同一类型。
另请参阅: