Изучение конструкции if let в Rust

Beginner

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

Введение

В этом лабе мы исследуем использование конструкции if let в Rust, которая позволяет писать более чистый код при сопоставлении перечислений и дает возможность указать варианты неудачи. Также мы демонстрируем, как if let можно использовать для сопоставления любого значения перечисления, включая варианты без параметров. Кроме того, мы приводим пример исправления кода, который пытается сравнить два значения перечисления, используя if let вместо этого.

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

if let

Для некоторых случаев использования при сопоставлении перечислений конструкция match оказывается неудобной. Например:

// Создаем `optional` типа `Option<i32>`
let optional = Some(7);

match optional {
    Some(i) => {
        println!("Это действительно длинная строка и `{:?}`", i);
        // ^ Нужно было два отступа, чтобы можно было деструктурировать
        // `i` из варианта.
    },
    _ => {},
    // ^ Это необходимо, потому что `match` должен быть исчерпывающим.
    // Не кажется, что это лишний пробел?
};

Для этого случая использования конструкция if let более проста, и кроме того, позволяет задавать различные варианты неудачи:

fn main() {
    // Все имеют тип `Option<i32>`
    let number = Some(7);
    let letter: Option<i32> = None;
    let emoticon: Option<i32> = None;

    // Конструкция `if let` читается так: "если `let` деструктурирует `number` в
    // `Some(i)`, то выполнить блок (`{}`).
    if let Some(i) = number {
        println!("Совпало {:?}!", i);
    }

    // Если нужно указать вариант неудачи, используйте `else`:
    if let Some(i) = letter {
        println!("Совпало {:?}!", i);
    } else {
        // Деструктуризация не удалась. Переходим к варианту неудачи.
        println!("Не совпало с числом. Перейдем к букве!");
    }

    // Задаем измененное условие неудачи.
    let i_like_letters = false;

    if let Some(i) = emoticon {
        println!("Совпало {:?}!", i);
    // Деструктуризация не удалась. Проверяем условие `else if`, чтобы понять,
    // следует ли перейти к альтернативному варианту неудачи:
    } else if i_like_letters {
        println!("Не совпало с числом. Перейдем к букве!");
    } else {
        // Условие оказалось ложным. Эта ветка является стандартной:
        println!("Я не люблю буквы. Перейдем к смайлику :)!");
    }
}

Таким же образом, if let можно использовать для сопоставления любого значения перечисления:

// Наш пример перечисления
enum Foo {
    Bar,
    Baz,
    Qux(u32)
}

fn main() {
    // Создаем примерные переменные
    let a = Foo::Bar;
    let b = Foo::Baz;
    let c = Foo::Qux(100);

    // Переменная a соответствует Foo::Bar
    if let Foo::Bar = a {
        println!("a - это foobar");
    }

    // Переменная b не соответствует Foo::Bar
    // Поэтому ничего не выведется
    if let Foo::Bar = b {
        println!("b - это foobar");
    }

    // Переменная c соответствует Foo::Qux, который имеет значение
    // Аналогично Some() в предыдущем примере
    if let Foo::Qux(value) = c {
        println!("c равно {}", value);
    }

    // Связывание также работает с `if let`
    if let Foo::Qux(value @ 100) = c {
        println!("c равно сто");
    }
}

Еще одно преимущество在于,что if let позволяет нам сопоставлять варианты перечислений без параметров. Это верно даже в случаях, когда перечисление не реализует или не выводит PartialEq. В таких случаях if Foo::Bar == a не скомпилируется, потому что экземпляры перечисления не могут быть сравнены, однако if let продолжит работать.

Вы готовы к вызову? Исправьте следующий пример, используя if let:

// Это перечисление специально не реализует и не выводит PartialEq.
// Именно поэтому сравнение Foo::Bar == a ниже приводит к ошибке компиляции.
enum Foo {Bar}

fn main() {
    let a = Foo::Bar;

    // Переменная a соответствует Foo::Bar
    if Foo::Bar == a {
    // ^-- это вызывает ошибку компиляции. Используйте `if let` вместо этого.
        println!("a - это foobar");
    }
}

Резюме

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