What is the correct error part of the Result type of a function propagating different error types?

假如想象 提交于 2021-01-29 13:12:31

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!