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.

  1. #![allow(dead_code)]
  2. #[derive(Debug)] enum Food { Apple, Carrot, Potato }
  3. #[derive(Debug)] struct Peeled(Food);
  4. #[derive(Debug)] struct Chopped(Food);
  5. #[derive(Debug)] struct Cooked(Food);
  6. // Peeling food. If there isn't any, then return `None`.
  7. // Otherwise, return the peeled food.
  8. fn peel(food: Option<Food>) -> Option<Peeled> {
  9. match food {
  10. Some(food) => Some(Peeled(food)),
  11. None => None,
  12. }
  13. }
  14. // Chopping food. If there isn't any, then return `None`.
  15. // Otherwise, return the chopped food.
  16. fn chop(peeled: Option<Peeled>) -> Option<Chopped> {
  17. match peeled {
  18. Some(Peeled(food)) => Some(Chopped(food)),
  19. None => None,
  20. }
  21. }
  22. // Cooking food. Here, we showcase `map()` instead of `match` for case handling.
  23. fn cook(chopped: Option<Chopped>) -> Option<Cooked> {
  24. chopped.map(|Chopped(food)| Cooked(food))
  25. }
  26. // A function to peel, chop, and cook food all in sequence.
  27. // We chain multiple uses of `map()` to simplify the code.
  28. fn process(food: Option<Food>) -> Option<Cooked> {
  29. food.map(|f| Peeled(f))
  30. .map(|Peeled(f)| Chopped(f))
  31. .map(|Chopped(f)| Cooked(f))
  32. }
  33. // Check whether there's food or not before trying to eat it!
  34. fn eat(food: Option<Cooked>) {
  35. match food {
  36. Some(food) => println!("Mmm. I love {:?}", food),
  37. None => println!("Oh no! It wasn't edible."),
  38. }
  39. }
  40. fn main() {
  41. let apple = Some(Food::Apple);
  42. let carrot = Some(Food::Carrot);
  43. let potato = None;
  44. let cooked_apple = cook(chop(peel(apple)));
  45. let cooked_carrot = cook(chop(peel(carrot)));
  46. // Let's try the simpler looking `process()` now.
  47. let cooked_potato = process(potato);
  48. eat(cooked_apple);
  49. eat(cooked_carrot);
  50. eat(cooked_potato);
  51. }

See also:

closures, Option, Option::map()