Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Розпакування варіантів і значень за замовчуванням

Існує більше ніж один спосіб розпакувати Option і перейти до значення за замовчуванням, якщо він None. Щоб вибрати той, що відповідає нашим потребам, нам потрібно врахувати таке:

  • чи потрібне нам жадібне або ліниве обчислення?
  • чи потрібно нам зберегти початкове порожнє значення без змін, або змінити його на місці?

or() є ланцюжковим, обчислюється жадібно, зберігає порожнє значення без змін

or() є ланцюжковим і жадібно обчислює свій аргумент, як показано в наведеному нижче прикладі. Зверніть увагу, що через жадібне обчислення аргументів or, змінна, передана до or, переміщується.

#[derive(Debug)]
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }

fn main() {
    let apple = Some(Fruit::Apple);
    let orange = Some(Fruit::Orange);
    let no_fruit: Option<Fruit> = None;

    let first_available_fruit = no_fruit.or(orange).or(apple);
    println!("first_available_fruit: {:?}", first_available_fruit);
    // first_available_fruit: Some(Orange)

    // `or` переміщує свій аргумент.
    // У прикладі вище `or(orange)` повернув `Some`, тому `or(apple)` не було викликано.
    // Але змінна з іменем `apple` усе одно була переміщена, і більше не може бути використана.
    // println!("Variable apple was moved, so this line won't compile: {:?}", apple);
    // TODO: розкоментуйте рядок вище, щоб побачити помилку компілятора
}

or_else() є ланцюжковим, обчислюється ліниво, зберігає порожнє значення без змін

Іншою альтернативою є використання or_else, який також є ланцюжковим і обчислюється ліниво, як показано в наведеному нижче прикладі:

#[derive(Debug)]
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }

fn main() {
    let no_fruit: Option<Fruit> = None;
    let get_kiwi_as_fallback = || {
        println!("Providing kiwi as fallback");
        Some(Fruit::Kiwi)
    };
    let get_lemon_as_fallback = || {
        println!("Providing lemon as fallback");
        Some(Fruit::Lemon)
    };

    let first_available_fruit = no_fruit
        .or_else(get_kiwi_as_fallback)
        .or_else(get_lemon_as_fallback);
    println!("first_available_fruit: {:?}", first_available_fruit);
    // Providing kiwi as fallback
    // first_available_fruit: Some(Kiwi)
}

get_or_insert() обчислюється жадібно, змінює порожнє значення на місці

Щоб переконатися, що Option містить значення, ми можемо використати get_or_insert, щоб змінити його на місці за допомогою значення за замовчуванням, як показано в наведеному нижче прикладі. Зверніть увагу, що get_or_insert жадібно обчислює свій параметр, тож змінна apple переміщується:

#[derive(Debug)]
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }

fn main() {
    let mut my_fruit: Option<Fruit> = None;
    let apple = Fruit::Apple;
    let first_available_fruit = my_fruit.get_or_insert(apple);
    println!("first_available_fruit is: {:?}", first_available_fruit);
    println!("my_fruit is: {:?}", my_fruit);
    // first_available_fruit is: Apple
    // my_fruit is: Some(Apple)
    //println!("Variable named `apple` is moved: {:?}", apple);
    // TODO: розкоментуйте рядок вище, щоб побачити помилку компілятора
}

get_or_insert_with() обчислюється ліниво, змінює порожнє значення на місці

Замість того щоб явно надавати значення за замовчуванням, ми можемо передати замикання до get_or_insert_with, ось так:

#[derive(Debug)]
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }

fn main() {
    let mut my_fruit: Option<Fruit> = None;
    let get_lemon_as_fallback = || {
        println!("Providing lemon as fallback");
        Fruit::Lemon
    };
    let first_available_fruit = my_fruit
        .get_or_insert_with(get_lemon_as_fallback);
    println!("first_available_fruit is: {:?}", first_available_fruit);
    println!("my_fruit is: {:?}", my_fruit);
    // Providing lemon as fallback
    // first_available_fruit is: Lemon
    // my_fruit is: Some(Lemon)

    // Якщо `Option` має значення, він залишається без змін, і замикання не викликається
    let mut my_apple = Some(Fruit::Apple);
    let should_be_apple = my_apple.get_or_insert_with(get_lemon_as_fallback);
    println!("should_be_apple is: {:?}", should_be_apple);
    println!("my_apple is unchanged: {:?}", my_apple);
    // Нижче наведено вивід. Зверніть увагу, що замикання `get_lemon_as_fallback` не викликається
    // should_be_apple is: Apple
    // my_apple is unchanged: Some(Apple)
}

Дивіться також:

closures, get_or_insert, get_or_insert_with, moved variables, or, or_else