Введение
Структура объединяет поля, которые всегда относятся друг к другу. Перечисление (enum) описывает значение, которое может иметь один из нескольких вариантов, причём каждый вариант может содержать свои данные. Выражение match в Rust позволяет явно обработать каждый из этих вариантов.
В этой лабораторной работе вы завершите определение перечисления сообщений, извлечёте координаты из одного из вариантов и расширите модель новым вариантом. Компилятор специально покажет, почему перед запуском обработчика сообщений необходимо охватить все возможные варианты.
Определите варианты с данными
На этом шаге вы определите три варианта одного типа Message.
Перейдите в подготовленный проект и откройте исходный файл:
cd /home/labex/project/message-processor
nano src/main.rs
Объявление перечисления содержит варианты внутри фигурных скобок. Код создаёт вариант, указывая имя перечисления, :: и имя варианта. Разные варианты одного перечисления могут содержать данные разных типов.
Замените комментарий Step 1 следующим кодом:
Text(String),
Move { x: i32, y: i32 },
Stop,
Text(String) содержит одно безымянное значение. Такой вариант Rust называет кортежеподобным вариантом. Move { x, y } содержит именованные поля, поэтому это структуроподобный вариант. Stop не содержит дополнительных данных и называется вариантом-единицей. Сначала важнее запомнить различие: «одно безымянное значение, именованные поля или отсутствие данных». Формальные названия можно выучить позже. Несмотря на различия в форме, каждое созданное значение имеет один тип — Message.
Сохраните файл с помощью Ctrl+O, нажмите Enter, затем выйдите из nano с помощью Ctrl+X. Проверьте и запустите подготовленный обработчик:
cargo check
cargo run --quiet
Вывод должен быть таким:
Text: Maintenance at 9
Move received
Stop requested
Подготовленный match выбирает одну ветвь для каждого варианта. Каждая ветвь имеет форму pattern => behavior, а короткие ветви разделяются запятыми. Шаблон Text(text) присваивает имя содержащейся строке. В ветви Move также задаются имена x и y, после чего они временно объединяются в (x, y). Скобки здесь создают кортеж из двух значений, который используется только как подготовленная заготовка; изучение кортежей не входит в цель этой лабораторной работы. Имя _coordinates начинается с _, поэтому Rust считает намеренное неиспользование этого временного значения. Благодаря этому первый запуск не выдаёт предупреждений. На следующем шаге вы замените эту заготовку и выведете обе координаты.
Используйте деструктурированные данные варианта
На этом шаге вы измените ветвь Move в match, чтобы координаты, извлечённые шаблоном, появились в выводе.
Шаблоны могут извлекать части варианта, которому они соответствуют. В структуроподобном варианте запись имён полей создаёт привязки, которые доступны выражению внутри ветви.
Откройте исходный файл:
nano src/main.rs
Замените всю ветвь Move следующим кодом:
Message::Move { x, y } => println!("Move to ({x}, {y})"),
Когда приходит сообщение Move, шаблон связывает сохранённые поля с локальными именами x и y. Выражение печати использует эти имена; отдельный доступ к полям или преобразование типа не требуется.
Сохраните файл и выйдите из редактора, затем выполните:
cargo run --quiet
Теперь средняя строка должна выглядеть так:
Move to (3, -2)
Отрицательное значение y подтверждает, что ветвь использовала данные фактического варианта, а не выводила фиксированные координаты.
Расширьте исчерпывающий match
На этом шаге вы добавите сообщение Pause, увидите проверку исчерпывающего сопоставления компилятором, а затем обработаете новый вариант.
match должен охватывать каждое возможное значение перечисления. Поэтому после добавления нового варианта компилятор указывает на каждый match, в котором нужно принять решение для этого варианта.
Откройте файл и добавьте следующий вариант сразу после Stop:
nano src/main.rs
Pause(u32),
Сохраните файл и выйдите из редактора, затем проверьте программу. Ошибка на этом этапе ожидаема:
cargo check
Найдите основную часть диагностического сообщения:
error[E0004]: non-exhaustive patterns: `Message::Pause(_)` not covered
Компилятор указывает на match message, потому что перечисление теперь может содержать значение Pause, но ни одна ветвь не объясняет, что с ним делать.
Снова откройте исходный файл. Добавьте эту ветвь ниже ветви Stop:
Message::Pause(seconds) => println!("Pause for {seconds} seconds"),
Затем удалите начальные // из подготовленной строки process(Message::Pause(5)); в функции main. Шаблон присваивает содержащемуся значению типа u32 локальное имя seconds.
Сохраните файл и выйдите из редактора, затем выполните проверку и запуск:
cargo check
cargo run --quiet
Полный вывод должен быть таким:
Text: Maintenance at 9
Move to (3, -2)
Stop requested
Pause for 5 seconds
Успешная проверка подтверждает, что match снова является исчерпывающим, а четвёртая строка показывает, что новая ветвь извлекла длительность паузы из варианта Pause.
Итоги
Вы определили варианты перечисления-единицы, кортежеподобные и структуроподобные варианты; извлекли содержащиеся данные с помощью шаблонов match; а после добавления новой альтернативы использовали ошибку компилятора E0004, чтобы сделать match исчерпывающим.


