Разбор последствий: почему ветвь match с дополнительным условием не считается Rust полностью покрывающей соответствующий шаблон?
Охраняемая ветвь match не считается безусловным покрытием шаблона. Если шаблон совпал, но его дополнительное условие оказалось ложным, Rust продолжает проверять следующие ветви. Поэтому для гарантии исчерпывающего сопоставления нужен неохраняемый шаблон, покрывающий оставшиеся случаи.
Охранные условия появились как способ выразить проверку, которая зависит не только от формы значения, но и от вычисляемого логического условия. Это позволяет сохранить структурное сопоставление в match, не разбивая логику на отдельный if после извлечения данных из шаблона.
Такой подход разделяет две задачи: шаблон проверяет структуру и связывает части значения, а guard выполняет дополнительную проверку. Компилятор не пытается доказать произвольное логическое условие, поэтому не может считать охраняемую ветвь гарантированным покрытием.
Если разработчик принимает ветвь с guard за полное покрытие шаблона, он может ошибочно ожидать, что match обработает все значения. При ложном условии совпавший шаблон не завершает сопоставление, и выполнение переходит к следующим ветвям.
Отсутствие безусловной запасной ветви приводит к ошибке исчерпывающего сопоставления на этапе компиляции. Это защищает от потенциальной паники при выполнении, но требует явно описать поведение для случаев, которые не подтверждены guard.
Сначала Rust проверяет ветви сверху вниз. Для каждой ветви он сопоставляет значение с шаблоном; если шаблон подходит, вычисляется guard. При истинном условии выбирается эта ветвь, а при ложном Rust продолжает поиск следующего совпадения.
Для анализа исчерпывающего сопоставления guard фактически не добавляет гарантии покрытия. Даже если условие выглядит очевидным для человека, компилятор рассматривает охраняемую ветвь как потенциально неприменимую.
В примере первая ветвь обрабатывает только Code(200). Значение Code(404) совпадает с её шаблоном Event::Code(code), но не проходит guard, поэтому проверяется следующая ветвь Event::Code(_). Именно неохраняемая вторая ветвь обеспечивает покрытие остальных кодов.
Guard может обращаться к связанным переменным и выполнять произвольное условие без побочных ограничений, связанных с самим синтаксисом шаблона. Однако сложная логика в guard ухудшает читаемость и не помогает компилятору доказать полноту; для важных инвариантов лучше явно представить случаи отдельными вариантами типа или добавить запасную ветвь.
В обработчике событий нужно считать код ответа 200 успешным, а остальные коды — ошибочными. Один вариант — использовать охраняемую ветвь для 200 и отдельную неохраняемую ветвь для остальных кодов. Плюс такого решения — компактная запись и сохранение доступа к извлечённому значению; минус — guard не участвует в доказательстве полноты.
Второй вариант — сначала сопоставить только структуру события, а затем использовать обычное условие внутри тела ветви. Это может быть удобнее при сложной бизнес-логике, но добавляет вложенность и размывает границу между структурным сопоставлением и проверкой условия.
Практически выбирают guard для короткого и очевидного условия, а для остальных значений добавляют явную неохраняемую ветвь. В результате компилятор контролирует полноту, а поведение при ложном условии остаётся предсказуемым.
Вопрос: Что произойдёт, если охраняемая ветвь стоит перед безусловной ветвью с тем же шаблоном?
Ответ: При истинном guard будет выбрана первая ветвь. При ложном условии Rust перейдёт к следующей, безусловной ветви. Поэтому такая конструкция корректна, если первая ветвь уточняет особый случай, а вторая обрабатывает остальные значения.
Вопрос: Может ли guard сделать сопоставление неисчерпывающим, даже если его условие всегда истинно фактически?
Ответ: Да. Компилятор не обязан доказывать истинность произвольного выражения guard. Даже условие, которое логически всегда истинно в конкретной версии программы, не превращает охраняемую ветвь в гарантированное покрытие; требуется другая неохраняемая ветвь или иной полный набор шаблонов.
Вопрос: В каком порядке вычисляются guards разных ветвей?
Ответ: В порядке объявления ветвей, сверху вниз, но только после успешного сопоставления шаблона соответствующей ветви. Guard нижней ветви не вычисляется, если предыдущая ветвь уже выбрана, и не вычисляется для ветви, чей шаблон не совпал. Поэтому guards с побочными эффектами могут зависеть от порядка ветвей, что является дополнительной причиной держать их простыми и без побочных эффектов.