Распаковка вариантов и значений по умолчанию

RustRustBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом лабе мы исследуем разные способы распаковать Option и вернуться к значению по умолчанию, если оно равно None, учитывая такие факторы, как eager или lazy evaluation и то, следует ли изменять пустое значение на месте или оставлять его неизменным.

Примечание: Если в лабе не указано имя файла, вы можете использовать любое имя файла, которое хотите. Например, вы можете использовать main.rs, скомпилировать и запустить его с помощью rustc main.rs &&./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/MemorySafetyandManagementGroup(["Memory Safety and Management"]) rust(("Rust")) -.-> rust/AdvancedTopicsGroup(["Advanced Topics"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/BasicConceptsGroup -.-> rust/mutable_variables("Mutable Variables") rust/DataTypesGroup -.-> rust/string_type("String Type") rust/DataTypesGroup -.-> rust/type_casting("Type Conversion and Casting") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/MemorySafetyandManagementGroup -.-> rust/lifetime_specifiers("Lifetime Specifiers") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") rust/AdvancedTopicsGroup -.-> rust/operator_overloading("Traits for Operator Overloading") subgraph Lab Skills rust/variable_declarations -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} rust/mutable_variables -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} rust/string_type -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} rust/type_casting -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} rust/function_syntax -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} rust/expressions_statements -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} rust/lifetime_specifiers -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} rust/method_syntax -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} rust/operator_overloading -.-> lab-99238{{"Распаковка вариантов и значений по умолчанию"}} end

Распаковка вариантов и значений по умолчанию

Есть несколько способов распаковать Option и вернуться к значению по умолчанию, если оно равно None. Чтобы выбрать подходящий для наших нужд, нужно рассмотреть следующие аспекты:

  • нужна eager или lazy evaluation?
  • нужно ли оставить исходное пустое значение неизменным или изменить его на месте?

or() можно цеплять, eager evaluation, пустое значение остается неизменным

or() можно цеплять и eager evaluation аргумента, как показано в следующем примере. Обратите внимание, что поскольку аргументы or eager evaluation, переменная, переданная в 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() можно цеплять, lazy evaluation, пустое значение остается неизменным

Другой вариант — использовать or_else, который также можно цеплять и lazy evaluation, как показано в следующем примере:

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

fn main() {
    let apple = Some(Fruit::Apple);
    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() eager evaluation, изменяет пустое значение на месте

Чтобы убедиться, что Option содержит значение, можно использовать get_or_insert, чтобы изменить его на месте с помощью значения по умолчанию, как показано в следующем примере. Обратите внимание, что get_or_insert eager evaluation своего параметра, поэтому переменная 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() lazy evaluation, изменяет пустое значение на месте

Вместо явного указания значения по умолчанию можно передать замыкание в 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)
}

Резюме

Поздравляем! Вы завершили лабу по Распаковке вариантов и значений по умолчанию. Вы можете практиковаться в других лабах в LabEx, чтобы улучшить свои навыки.