Option и unwrap

В последнем примере мы показали, что можем вызвать сбой программы по своему желанию. Мы сказали нашей программе вызвать panic, если мы выпьем сладкий лимонад. Но что, если ожидаем какой-то напиток, но не получаем его? Этот случай тоже плохой, так что и он должен быть обработан!

Мы могли бы сравнить значение с пустой строкой ("") так же, как мы сделали это с лимонадом. Поскольку мы используем Rust, пусть компилятор сам укажет нам случаи, когда напитка нет.

Перечисление (enum) из стандартной библиотеки (std), называющееся Option<T>, используется, когда значение может отсутствовать. Оно может находиться в одном из двух состояний:

  • Some(T): элемент типа T найден
  • None: элемент не найден

Эти случаи могут быть или явно обработаны с помощью match, или неявно с unwrap. Неявная обработка либо вернёт внутренний элемент, либо вызовет panic.

Обратите внимание, что можно вручную настроить сообщение выдаваемое при панике с помощью expect, в противном случае unwrap оставляет нам менее понятный вывод, чем явная обработка. В следующем примере явная обработка даёт более контролируемый результат, при этом сохраняется возможность паниковать, если это необходимо.

// Взрослый человек всё это видео, и может хорошо справиться с любым напитком.
// Все напитки обрабатываются явно, с использованием конструкции `match`.
fn give_adult(drink: Option<&str>) { // выдать взрослому напиток
    // Укажем что нужно делать, в каждом случае.
    match drink {
        Some("лимонад") => println!("Фи! Слишком сладко."),
        Some(inner)   => println!("{}? Хорошо.", inner),
        None          => println!("Нет напитка? Ну что ж."),
    }
}

// Другие будут паниковать перед тем как выпить напитки, содержащие сахар.
// Все напитки обрабатываются неявно, с использованием `unwrap`.
fn drink(drink: Option<&str>) {
    // `unwrap` возвращает `panic` когда получает на вход `None`.
    let inside = drink.unwrap();
    if inside == "лимонад" { panic!("AAAaaaaa!!!!"); }

    println!("Я люблю {}ы!!!!!", inside);
}

fn main() {
    let water  = Some("вода");
    let lemonade = Some("лимонад");
    let void  = None;

    give_adult(water);
    give_adult(lemonade);
    give_adult(void);

    let coffee = Some("кофе");
    let nothing = None;

    drink(coffee);
    drink(nothing);
}