Enum (перечисления) в Rust кардинально отличаются от enum в C/C++: они способны хранить ассоциированные данные и идеально подходят для моделирования состояния и ошибок. С их помощью строят типобезопасные finite-state machines, различные виды Option/Result, реализуют паттерн "sum types". Исторически аналогичные конструкции применялись в функциональных языках для описания вариантов сущности со строго разнесёнными вариантами.
Проблема: добиться выразительности (выразить все варианты состояния), где каждый случай обработки обязателен, и невозможно случайно пропустить ветку. Ошибки project-wide сложно типизировать без такой выразительной структуры.
Решение: enum с ассоциированными данными и pattern matching дают контроль — каждая ветка проверяется компилятором, exhaustiveness обеспечивается. Кроме того, для Result и Option уже реализована масса вспомогательных методов.
Пример кода:
enum NetworkState { Disconnected, Connecting(u32), // попытка номер Connected(String), Error(String), } fn print_state(state: NetworkState) { match state { NetworkState::Disconnected => println!("Net: disconnected"), NetworkState::Connecting(count) => println!("Net: connecting (attempt {})", count), NetworkState::Connected(addr) => println!("Net: connected to {}", addr), NetworkState::Error(msg) => println!("Net error: {}", msg), } }
Ключевые особенности:
Можно ли частично обрабатывать ветки enum без _?
Компилятор запрещает незакрытые случаи для non-exhaustive enum, но если использовать _, то необработанные ветки будут "поглощены". Следует избегать _ при клинически важных ветках, чтобы future изменения не остались незамеченными.
В каких случаях ассоциированные значения ссылаются, а в каких копируются при pattern matching?
При pattern matching ассоциированные данные по умолчанию перемещаются (move). Если нужен только просмотр, используйте ссылки:
match &state { NetworkState::Connected(addr) => println!("by ref: {}", addr), _ => {} }
Можно ли в одной структуре использовать два enum с перекрывающимися вариантами по имени?
Можно, но имена вариантов используются с префиксом enum. Это исключает коллизии и делает код самодокументируемым (например, Status::Ok vs NetworkState::Ok).
_ для скрытия всё новых вариантов при расширении enum_ =>) в критичных обработчиках ошибокВ коде обработка Result<T, E> всегда имеет catch-all через _ =>, и новые ошибки (при расширении enum) проходят мимо — случаются silent-loss ошибок.
Плюсы:
Минусы:
Используются exhaustiveness matching, каждый вариант Enum обрабатывается явно, либо panic в ветке, для которой нет устойчивого поведения.
Плюсы:
Минусы: