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

}

}

fn main() {

let p = Person {

job: Some(Job {

phone_number: Some(PhoneNumber {

area_code: Some(61),

number: 439222222,

}),

}),

};

assert_eq!(p.work_phone_area_code(), Some(61));

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Combinators:map

match is a valid method for handling Options. However, you may eventually find heavy usage tedious, especially with operations only valid with an input. In these cases, combinators can be used to manage control flow in a modular fashion.

Option has a built in method called map(), a combinator for the simple mapping of Some -> Some and None -> None. Multiple map() calls can be chained together for even more flexibility.

In the following example, process() replaces all functions previous to it while staying compact.

#![allow(dead_code)]

#[derive(Debug)] enum Food { Apple, Carrot, Potato }

#[derive(Debug)] struct Peeled(Food);

#[derive(Debug)] struct Chopped(Food);

#[derive(Debug)] struct Cooked(Food);

// Peeling food. If there isn't any, then return `None`.

// Otherwise, return the peeled food.

fn peel(food: Option) -> Option {

match food {

Some(food) => Some(Peeled(food)),

None       => None,

}

}

// Chopping food. If there isn't any, then return `None`.

// Otherwise, return the chopped food.

fn chop(peeled: Option) -> Option {

match peeled {

Some(Peeled(food)) => Some(Chopped(food)),

None               => None,

}

}

// Cooking food. Here, we showcase `map()` instead of `match` for case handling.

fn cook(chopped: Option) -> Option {

chopped.map(|Chopped(food)| Cooked(food))

}

// A function to peel, chop, and cook food all in sequence.

// We chain multiple uses of `map()` to simplify the code.

fn process(food: Option) -> Option {

food.map(|f| Peeled(f))

.map(|Peeled(f)| Chopped(f))

.map(|Chopped(f)| Cooked(f))

}

// Check whether there's food or not before trying to eat it!

fn eat(food: Option) {

match food {

Some(food) => println!("Mmm. I love {:?}", food),

None       => println!("Oh no! It wasn't edible."),

}

}

fn main() {

let apple = Some(Food::Apple);

let carrot = Some(Food::Carrot);

let potato = None;

let cooked_apple = cook(chop(peel(apple)));

let cooked_carrot = cook(chop(peel(carrot)));

// Let's try the simpler looking `process()` now.

let cooked_potato = process(potato);

eat(cooked_apple);

eat(cooked_carrot);

eat(cooked_potato);

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

See also:

closures, Option, Option::map()

Combinators:and_then

map() was described as a chainable way to simplify match statements. However, using map() on a function that returns an Option results in the nested Option>. Chaining multiple calls together can then become confusing. That's where another combinator called and_then(), known in some languages as flatmap, comes in.

and_then() calls its function input with the wrapped value and returns the result. If the Option is None, then it returns None instead.

In the following example, cookable_v2() results in an Option. Using map() instead of and_then() would have given an Option>, which is an invalid type for eat().

#![allow(dead_code)]

#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }

#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }

// We don't have the ingredients to make Sushi.

fn have_ingredients(food: Food) -> Option {

match food {

Food::Sushi => None,

_           => Some(food),

}

}

// We have the recipe for everything except Cordon Bleu.

fn have_recipe(food: Food) -> Option {

match food {

Food::CordonBleu => None,

_                => Some(food),

}

}

// To make a dish, we need both the recipe and the ingredients.

// We can represent the logic with a chain of `match`es:

fn cookable_v1(food: Food) -> Option {

match have_recipe(food) {

None       => None,

Some(food) => match have_ingredients(food) {

None       => None,

Some(food) => Some(food),

},

}

}

// This can conveniently be rewritten more compactly with `and_then()`:

fn cookable_v2(food: Food) -> Option {

have_recipe(food).and_then(have_ingredients)

}

fn eat(food: Food, day: Day) {

match cookable_v2(food) {

Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food),

None       => println!("Oh no. We don't get to eat on {:?}?", day),

}

}

fn main() {

let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi);

eat(cordon_bleu, Day::Monday);

eat(steak, Day::Tuesday);

eat(sushi, Day::Wednesday);

}