# Options

* Type `Option` represents an optional value: every Option is either `Some` and contains a value, or `None`.

  ```rust
  enum Option<T> {
    Some(T),
    None,
  }
  ```
* The `if let` syntax lets you combine `if` and `let` into a less verbose way to handle values that match one pattern while ignoring the rest.
* Using `if let` means less typing, less indentation, and less boilerplate code.
* Similarly `while let` syntax lets you combine `while` and `let`.
* References:
  * [Option Enum Format](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions)
  * [Option Module Documentation](https://doc.rust-lang.org/std/option/)
  * [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html)
  * [Concise Control Flow with `if let`](https://doc.rust-lang.org/book/ch06-03-if-let.html)
  * [while let](https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html)

## options1.rs

```rust
// This function returns how much icecream there is left in the fridge.
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
// someone eats it all, so no icecream is left (value 0). Return `None` if
// `hour_of_day` is higher than 23.
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
    // Complete the function body.
    match hour_of_day {
        0..=21 => Some(5),
        22..=23 => Some(0),
        _ => None,
    }
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn raw_value() {
        // Fix this test. How do you get the value contained in the
        // Option?
        let icecreams = maybe_icecream(12).unwrap();

        assert_eq!(icecreams, 5); // Don't change this line.
    }

    #[test]
    fn check_icecream() {
        assert_eq!(maybe_icecream(0), Some(5));
        assert_eq!(maybe_icecream(9), Some(5));
        assert_eq!(maybe_icecream(18), Some(5));
        assert_eq!(maybe_icecream(22), Some(0));
        assert_eq!(maybe_icecream(23), Some(0));
        assert_eq!(maybe_icecream(24), None);
        assert_eq!(maybe_icecream(25), None);
    }
}
```

* In this exercise we need to return ice cream left by given hour.
  * Hour `0` until `21` return `5` scoop left.
  * Hour `22` until `23` return `0` scoop left.
  * Hour `24` or more return `None`.
* So based on the condition above we can use `match` syntax like this:

  ```rust
  match hour_of_day {
      0..=21 => Some(5),
      22..=23 => Some(0),
      _ => None,
    }
  }
  ```
* And to get the value we can use `unwrap` method.
* `unwrap` can cause panic if the values is `None` so best not to use, we can use `unwrap_or` instead to provide default value if `None`.

## options2.rs

```rust
fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    #[test]
    fn simple_option() {
        let target = "rustlings";
        let optional_target = Some(target);

        if let Some(word) = optional_target {
            assert_eq!(word, target);
        }
    }

    #[test]
    fn layered_option() {
        let range = 10;
        let mut optional_integers: Vec<Option<i8>> = vec![None];

        for i in 1..=range {
            optional_integers.push(Some(i));
        }

        let mut cursor = range;

        // Make this a while-let statement. Remember that `Vec::pop()`
        // adds another layer of `Option`. You can do nested pattern matching
        // in if-let and while-let statements.
        while let Some(Some(integer)) = optional_integers.pop() {
            assert_eq!(integer, cursor);
            cursor -= 1;
        }

        assert_eq!(cursor, 0);
    }
}
```

* In this exercise we need to fix `simple_option` function by using `if let` syntax to safely unwrap/unpack the `Option` value.

  ```rust
  let Some(word) = optional_target {
    assert_eq!(word, target);
  }
  ```
* The code above will unwrap the `optional_target`.
  * If there is `Some` value it will bind it to variable `word` and we can use it inside the block.
  * If `None` then it will ignore it.
* Second task is to fix `simple_option` by using `while let` syntax.

  ```rust
  while let Some(Some(integer)) = optional_integers.pop() {
    assert_eq!(integer, cursor);
    cursor -= 1;
  }
  ```
* The code above will do same thing with `if let`, it will check the returned value of `optional_integers.pop()`.
  * If there is `Some` value there it will bind it to `integer` variable and then goes inside the block.
  * If `None` the loop will stop.

## options3.rs

```rust
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let optional_point = Some(Point { x: 100, y: 200 });

    // Fix the compiler error by adding something to this match statement.
    match &optional_point { // Add &
        Some(p) => println!("Co-ordinates are {},{}", p.x, p.y),
        _ => panic!("No match!"),
    }

    println!("{optional_point:?}"); // Don't change this line.
}
```

* In this exercise because we want to use the variables `optional_point` further after match we should not move the ownership.
* Instead we should match the reference so it can be borrowed by adding `&`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bagus-cahyono.gitbook.io/programming-notes/rust/rustlings_exercise/12_options.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
