问题
I tried to write a function, that shall propagate different error types. Only for example see the following code:
use std::fs::File;
use std::io;
use std::io::Read;
fn main() {
let number = read_number_from_file().unwrap();
println!("Read number {}!", number);
}
// So what is the correct error part of the Result<i32, ...>?
fn read_number_from_file() -> Result<i32, io::Error> {
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
let number = s.parse()?;
Ok(number)
}
Compiling it results in the following error unsurprisingly:
error[E0277]: `?` couldn't convert the error to `std::io::Error`
--> src\main.rs:16:27
|
16 | let number = s.parse()?;
| ^ the trait `std::convert::From<std::num::ParseIntError>` is not implemented for `std::io::Error`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<std::io::Error as std::convert::From<std::ffi::NulError>>
<std::io::Error as std::convert::From<std::io::ErrorKind>>
<std::io::Error as std::convert::From<std::io::IntoInnerError<W>>>
= note: required by `std::convert::From::from`
So what is the correct Error type of the Result
type? Is there an answer not only for this specific case: I have a function with several calls to other functions returning Result<T, E>
which in the error case shall be propagated out of the function to the caller but have different Types E
?
回答1:
You should define your own error type, which includes all possible errors that could happen:
#[derive(Debug)]
enum Error {
Io(io::Error),
ParseInt(num::ParseIntError)
}
impl From<io::Error> for Error {
fn from(other: io::Error) -> Error {
Error::Io(other)
}
}
impl From<num::ParseIntError> for Error {
fn from(other: num::ParseIntError) -> Error {
Error::ParseInt(other)
}
}
The From
conversions allow the ?
operator to work as expected.
You can save a lot of typing by using one of the several crates that will generate the boilerplate for you. Some popular ones are thiserror and snafu.
I prefer thiserror
because it only adds the implementations that you would otherwise write yourself and, if you are writing a library, it is transparent to your library's users. snafu
is arguably more powerful - it generates a lot more code, but is opinionated in what it generates, so you will need to get used to its paradigm in order to take full advantage of it, and the snafu concepts will become part of your library's API.
Using thiserror
, the code above is reduced to:
use thiserror::Error;
#[derive(Debug, Error)]
enum Error {
#[error("IO Error: {0})]
Io(#[from] io::Error),
#[error("ParseInt Error: {0})]
ParseInt(#[from] num::ParseIntError)
}
Note that this is also generating Display
implementations, which I didn't include above.
来源:https://stackoverflow.com/questions/62100863/what-is-the-correct-error-part-of-the-result-type-of-a-function-propagating-diff