if let
В некоторых случаях использование match
выглядит неуклюже. Например:
#![allow(unused)] fn main() { // Создаём переменную `optional` типа `Option<i32>` let optional = Some(7); match optional { Some(i) => { println!("Это очень большая строка и `{:?}`", i); // ^ Нужно 2 отступа только для того, чтобы извлечь `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 = Foo::Bar"); } // Переменная `b` не соответствует `Foo::Bar`. // Поэтому ничего не выведется на экран if let Foo::Bar = b { println!("b = Foo::Bar"); } // Переменная `c` соответствует `Foo::Qux`, которая имеет значение // аналогичное `Some()` как в предыдущем примере: if let Foo::Qux(value) = c { println!("c ={}", value); } // С `if let` также работает и привязка if let Foo::Qux(value @ 100) = c { println!("c = 100"); } }
Другое преимущество if let
в том, что он позволяет сопоставлять нам не параметризованные варианты перечисления. Это возможно даже если для перечисления не реализован и не выведен типаж PartialEq
. В некоторых случаях, if Foo::Bar == a
не скомпилируется, потому что экземпляры перечисления не могут быть равны. Однако, с if let
всё будет работать.
Хотите вызов? Исправьте следующий пример с использованием if let
:
// Для это перечисление намеренно не добавлен #[derive(PartialEq)], // и мы не реализовывали для него PartialEq. Вот почему сравнение Foo::Bar == a терпит неудачу. enum Foo {Bar} fn main() { let a = Foo::Bar; // Переменная соответствует Foo::Bar if Foo::Bar == a { // ^-- это вызовет ошибку компиляции. Используйте `if let` вместо этого. println!("a is foobar"); } }