Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust proper error handling (auto convert from one error type to another with question mark)

Tags:

rust

I want to learn how to properly deal with errors in Rust. I have read the book and this example; now I would like to know how I should deal with errors in this function:

fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> {     let url = self.root.join("/term/pv/synch"); // self.root is url::Url     let url = match url {         Ok(url) => url,         // ** this err here is url::ParseError and can be converted to Error::Kind https://docs.rs/reqwest/0.8.3/src/reqwest/error.rs.html#54-57 **//         Err(err) => {             return Err(Error {                 kind: ::std::convert::From::from(err),                 url: url.ok(),             })         }     };      Ok(reqwest::get(url)?.json()?) //this return reqwest::Error or convert to pv::sych::MeasPeriods automaticly }       

This code is improper; it causes a compilation error:

error[E0451]: field `kind` of struct `reqwest::Error` is private   --> src/main.rs:34:42    | 34 |             Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `kind` is private  error[E0451]: field `url` of struct `reqwest::Error` is private   --> src/main.rs:34:81    | 34 |             Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})    |                                                                                 ^^^^^^^^^^^^^ field `url` is private 

What is a proper pattern to deal with that case? For me, reqwest::Error in this case is a good solution so I would like to avoid defining my own error type:

enum MyError {     Request(reqwest::Error),     Url(url::ParseError) // this already a part of request::Error::Kind!!! }  
like image 887
S.R Avatar asked Jan 24 '18 20:01

S.R


People also ask

What does the question mark do in Rust?

operator in Rust is used as an error propagation alternative to functions that return Result or Option types. The ? operator is a shortcut as it reduces the amount of code needed to immediately return Err or None from the types Result<T, Err> or Option in a function.

Does Rust have error handling?

Errors are a fact of life in software, so Rust has a number of features for handling situations in which something goes wrong. In many cases, Rust requires you to acknowledge the possibility of an error and take some action before your code will compile.


2 Answers

Update 2020

The rust programming language is evolving quickly so a new answer can be added! I really liked custom_error but now I think thiserror will be my loved one!

use thiserror::Error;  #[derive(Error, Debug)] pub enum DataStoreError {     #[error("data store disconnected")]     Disconnect(#[from] io::Error),     #[error("the data for key `{0}` is not available")]     Redaction(String),     #[error("invalid header (expected {expected:?}, found {found:?})")]     InvalidHeader {         expected: String,         found: String,     },     #[error("unknown data store error")]     Unknown, } 

This allow change io::Error to DataStoreError::Disconnect with question mark ?. Go here for details

useful links:

  • great blog about using thiserror in combine with anyhow

Other interesting crates:

  • anyhow - Flexible concrete Error type built on std::error::Error
  • snafu - Situation Normal: All Fouled Up - SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context. (similar to thiserror)
  • custom_error - This crate contains a macro that should make it easier to define custom errors without having to write a lot of boilerplate code.

for panics:

  • proc-macro-error - This crate aims to make error reporting in proc-macros simple and easy to use.
  • human-panic - Panic messages for humans. Handles panics by calling std::panic::set_hook to make errors nice for humans.
like image 112
S.R Avatar answered Oct 07 '22 22:10

S.R


Unfortunately, in your case you cannot create a reqwest::Error from other error types, if the reqwest library does not provide a way to do so (and it likely doesn't). To solve this problem, which is very common, especially in applications which use multiple libraries, the proper solution would be one of the following:

  1. Declare your own custom enum with all errors your application works with (or one subsystem of your application; granularity highly depends on the project), and declare From conversions from all errors you work with to this enum type.

    As an extension of this approach, you can use error-chain (or quick-error, on which error-chain is basically based) to generate such custom types and conversions in a semi-automatic way.

  2. Use a special, generic error type. There are basically two of them:

    a. Box<Error> where Error is defined in the standard library.

    b. Use the Error type defined in the failure crate.

    Then the question mark operator will be able to convert any compatible error to one of these types because of various Into and From trait implementations.

Note that the failure crate is intended to be the way to define errors promoted in the Rust community. Not only does it provide a common error type and trait (which fixes various issues with the std::error::Error trait; see for example here), it also has facilities to define your own error types (for example, with failure_derive), and for tracking error context, causes and generating backtrace. Additionally, it tries to be as compatible with the existing error handling approaches as possible, therefore it can be used to integrate with libraries which use other, older approaches (std::error::Error, error-chain, quick-error) quite easily. So I strongly suggest you to consider using this crate first, before other options.

I have already started using failure in my application projects, and I just can't express how much easier and nicer error handling has become. My approach is as follows:

  1. Define the Result type:

    type Result<T> = std::result::Result<T, failure::Error>; 
  2. Use Result<Something> everywhere where an error can be returned, using the question mark operator (?) to convert between errors and functions like err_msg or format_err! or bail! to create my own error messages.

I have yet to write a library using failure, but I imagine that for libraries it would be important to create more specific errors declared as an enum, which can be done with the failure_derive crate. For applications, though, the failure::Error type is more than enough.

like image 42
Vladimir Matveev Avatar answered Oct 07 '22 21:10

Vladimir Matveev