Rust by Example — страница 54 из 66

Result

We've seen that the Option enum can be used as a return value from functions that may fail, where None can be returned to indicate failure. However, sometimes it is important to express why an operation failed. To do this we have the Result enum.

The Result enum has two variants:

   • Ok(value) which indicates that the operation succeeded, and wraps the value returned by the operation. (value has type T)

   • Err(why), which indicates that the operation failed, and wraps why, which (hopefully) explains the cause of the failure. (why has type E)

mod checked {

// Mathematical "errors" we want to catch

#[derive(Debug)]

pub enum MathError {

DivisionByZero,

NonPositiveLogarithm,

NegativeSquareRoot,

}

pub type MathResult = Result;

pub fn div(x: f64, y: f64) -> MathResult {

if y == 0.0 {

// This operation would `fail`, instead let's return the reason of

// the failure wrapped in `Err`

Err(MathError::DivisionByZero)

} else {

// This operation is valid, return the result wrapped in `Ok`

Ok(x / y)

}

}

pub fn sqrt(x: f64) -> MathResult {

if x < 0.0 {

Err(MathError::NegativeSquareRoot)

} else {

Ok(x.sqrt())

}

}

pub fn ln(x: f64) -> MathResult {

if x <= 0.0 {

Err(MathError::NonPositiveLogarithm)

} else {

Ok(x.ln())

}

}

}

// `op(x, y)` === `sqrt(ln(x / y))`

fn op(x: f64, y: f64) -> f64 {

// This is a three level match pyramid!

match checked::div(x, y) {

Err(why) => panic!("{:?}", why),

Ok(ratio) => match checked::ln(ratio) {

Err(why) => panic!("{:?}", why),

Ok(ln) => match checked::sqrt(ln) {

Err(why) => panic!("{:?}", why),

Ok(sqrt) => sqrt,

},

},

}

}

fn main() {

// Will this fail?

println!("{}", op(1.0, 10.0));

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

?

Chaining results using match can get pretty untidy; luckily, the ? operator can be used to make things pretty again. ? is used at the end of an expression returning a Result, and is equivalent to a match expression, where the Err(err) branch expands to an early Err(From::from(err)), and the Ok(ok) branch expands to an ok expression.

mod checked {

#[derive(Debug)]

enum MathError {

DivisionByZero,

NonPositiveLogarithm,

NegativeSquareRoot,

}

type MathResult = Result;

fn div(x: f64, y: f64) -> MathResult {

if y == 0.0 {

Err(MathError::DivisionByZero)

} else {

Ok(x / y)

}

}

fn sqrt(x: f64) -> MathResult {

if x < 0.0 {

Err(MathError::NegativeSquareRoot)

} else {

Ok(x.sqrt())

}

}

fn ln(x: f64) -> MathResult {

if x <= 0.0 {

Err(MathError::NonPositiveLogarithm)

} else {

Ok(x.ln())

}

}

// Intermediate function

fn op_(x: f64, y: f64) -> MathResult {

// if `div` "fails", then `DivisionByZero` will be `return`ed

let ratio = div(x, y)?;

// if `ln` "fails", then `NonPositiveLogarithm` will be `return`ed

let ln = ln(ratio)?;

sqrt(ln)

}

pub fn op(x: f64, y: f64) {

match op_(x, y) {

Err(why) => panic!(match why {

MathError::NonPositiveLogarithm

=> "logarithm of non-positive number",

MathError::DivisionByZero

=> "division by zero",

MathError::NegativeSquareRoot

=> "square root of negative number",

}),

Ok(value) => println!("{}", value),

}

}

}

fn main() {

checked::op(1.0, 10.0);

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Be sure to check the documentation, as there are many methods to map/compose Result.

panic!

The panic! macro can be used to generate a panic and start unwinding its stack. While unwinding, the runtime will take care of freeing all the resources owned by the thread by calling the destructor of all its objects.

Since we are dealing with programs with only one thread, panic! will cause the program to report the panic message and exit.

// Re-implementation of integer division (/)

fn division(dividend: i32, divisor: i32) -> i32 {

if divisor == 0 {

// Division by zero triggers a panic

panic!("division by zero");

} else {

dividend / divisor

}

}

// The `main` task

fn main() {

// Heap allocated integer

let _x = Box::new(0i32);

// This operation will trigger a task failure

division(3, 0);

println!("This point won't be reached!");