Читаем Rust by Example полностью

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

<p id="boxing_errors"><strong><a l:href="#boxing_errors">Box</a><a l:href="#boxing_errors">ing errors</a></strong></p>

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.

The stdlib helps in boxing our errors by having Box implement conversion from any type that implements the Error trait into the trait object Box, via From.

use std::error;

use std::fmt;

// Change the alias to `Box`.

type Result = std::result::Result>;

#[derive(Debug, Clone)]

struct EmptyVec;

impl fmt::Display for EmptyVec {

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

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

}

}

impl error::Error for EmptyVec {}

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

vec.first()

.ok_or_else(|| EmptyVec.into()) // Converts to Box

.and_then(|s| {

s.parse::()

.map_err(|e| e.into()) // Converts to Box

.map(|i| 2 * i)

})

}

fn print(result: Result) {

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

<p id="see_also_63"><strong><a l:href="#see_also_63">See also:</a></strong></p>

Dynamic dispatch and Error trait

<p id="other_uses_of_"><strong><a l:href="#other_uses_of_">Other uses of</a><a l:href="#other_uses_of_">?</a></strong></p>

Notice in the previous example that our immediate reaction to calling parse is to map the error from a library error into a boxed error:

.and_then(|s| s.parse::()

.map_err(|e| e.into())

Since this is a simple and common operation, it would be convenient if it could be elided. Alas, because and_then is not sufficiently flexible, it cannot. However, we can instead use ?.

? was previously explained as either unwrap or return Err(err). This is only mostly true. It actually means unwrap or return Err(From::from(err)). Since From::from is a conversion utility between different types, this means that if you ? where the error is convertible to the return type, it will convert automatically.

Here, we rewrite the previous example using ?. As a result, the map_err will go away when From::from is implemented for our error type:

use std::error;

use std::fmt;

// Change the alias to `Box`.

type Result = std::result::Result>;

#[derive(Debug)]

struct EmptyVec;

impl fmt::Display for EmptyVec {

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

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

}

}

Перейти на страницу:

Похожие книги