Выбрать главу

The most basic way of handling mixed error types is to just embed them in each other.

use std::num::ParseIntError;

fn double_first(vec: Vec<&str>) -> Option<Result<i32, ParseIntError>> {

vec.first().map(|first| {

first.parse::<i32>().map(|n| 2 * n)

})

}

fn main() {

let numbers = vec!["42", "93", "18"];

let empty = vec![];

let strings = vec!["tofu", "93", "18"];

println!("The first doubled is {:?}", double_first(numbers));

println!("The first doubled is {:?}", double_first(empty));

// Error 1: the input vector is empty

println!("The first doubled is {:?}", double_first(strings));

// Error 2: the element doesn't parse to a number

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

There are times when we'll want to stop processing on errors (like with ?) but keep going when the Option is None. A couple of combinators come in handy to swap the Result and Option.

use std::num::ParseIntError;

fn double_first(vec: Vec<&str>) -> Result<Option<i32>, ParseIntError> {

let opt = vec.first().map(|first| {

first.parse::<i32>().map(|n| 2 * n)

});

opt.map_or(Ok(None), |r| r.map(Some))

}

fn main() {

let numbers = vec!["42", "93", "18"];

let empty = vec![];

let strings = vec!["tofu", "93", "18"];

println!("The first doubled is {:?}", double_first(numbers));

println!("The first doubled is {:?}", double_first(empty));

println!("The first doubled is {:?}", double_first(strings));

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Sometimes it simplifies the code to mask all of the different errors with a single type of error. We'll show this with a custom error.

Rust allows us to define our own error types. In general, a "good" error type:

   • Represents different errors with the same type

   • Presents nice error messages to the user

   • Is easy to compare with other types

      • Good: Err(EmptyVec)

      • Bad: Err("Please use a vector with at least one element".to_owned())

   • Can hold information about the error

      • Good: Err(BadChar(c, position))

      • Bad: Err("+ cannot be used here".to_owned())

   • Composes well with other errors

use std::fmt;

type Result<T> = std::result::Result<T, DoubleError>;

// Define our error types. These may be customized for our error handling cases.

// Now we will be able to write our own errors, defer to an underlying error

// implementation, or do something in between.

#[derive(Debug, Clone)]

struct DoubleError;

// Generation of an error is completely separate from how it is displayed.

// There's no need to be concerned about cluttering complex logic with the display style.

//

// Note that we don't store any extra info about the errors. This means we can't state

// which string failed to parse without modifying our types to carry that information.

impl fmt::Display for DoubleError {

fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

write!(f, "invalid first item to double")

}

}

fn double_first(vec: Vec<&str>) -> Result<i32> {

vec.first()

// Change the error to our new type.

.ok_or(DoubleError)

.and_then(|s| {

s.parse::<i32>()

// Update to the new error type here also.

.map_err(|_| DoubleError)

.map(|i| 2 * i)

})

}

fn print(result: Result<i32>) {

match result {

Ok(n) => println!("The first doubled is {}", n),

Err(e) => println!("Error: {}", e),

}

}

fn main() {

let numbers = vec!["42", "93", "18"];

let empty = vec![];

let strings = vec!["tofu", "93", "18"];

print(double_first(numbers));

print(double_first(empty));

print(double_first(strings));

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

A way to write simple code while preserving the original errors is to Box them. The drawback is that the underlying error type is only known at runtime and not statically determined.