Перечисления

В Rust есть «типы-суммы», или перечисления (тип-сумма — это термин из теории типов). Перечисления — это очень полезная возможность Rust, и они очень много используются в стандартной библиотеке языка. Они объявляются с помощью ключевого слова enum. enum — это тип, который соотносит набор неких вариантов одному имени. Например, ниже мы определяем перечисление Character (символ), представляющее собой или цифру (Digit), или что-то другое.

enum Character {
    Digit(i32),
    Other,
}

Большая часть обычных типов могут быть вариантами перечисления. Вот несколько примеров:

struct Empty;
struct Color(i32, i32, i32);
struct Length(i32);
struct Stats { Health: i32, Mana: i32, Attack: i32, Defense: i32 }
struct HeightDatabase(Vec<i32>);

Здесь мы видим, что, в зависимости от типа, вариант перечисления может содержать, а может и не содержать вложенные данные. Например, в перечислении Character, вариант Digit даёт значимое имя числу типа i32. А вот вариант Other представляет собой лишь имя, без значения. Однако наиболее полезно именно то, что отдельные варианты представляют собой различные виды символов.

Как и структуры, варианты перечислений по умолчанию не сравнимы операциями сравнения (==, !=), не упорядочены (не реализуют <, >= и другие) и не поддерживают другие двухместные операции, такие как умножение (*) и сложение (+). Нижеследующий код, как таковой, не верен (если мы используем приведённый выше тип-перечисление Character):

// Оба этих присваивания успешны
let ten  = Character::Digit(10);
let four = Character::Digit(4);

// Error: `*` is not implemented for type `Character`
let forty = ten * four;

// Error: `<=` is not implemented for type `Character`
let four_is_smaller = four <= ten;

// Error: `==` is not implemented for type `Character`
let four_equals_ten = four == ten;

Мы используем синтаксис :: чтобы использовать имя каждого из вариантов. Их область видимости ограничена именем самого перечисления enum. Это позволяет использовать оба варианта из примера ниже совместно:

Character::Digit(10);
Hand::Digit;

Оба варианта имеют одинаковые имена, Digit, но область видимости каждого из них ограничена соответствующим именем enum.

То, что пользовательские типы по умолчанию не поддерживают операции, может показаться довольно ограниченным. Но это ограничение, которое мы всегда можем преодолеть. Есть два способа: реализовать операцию самостоятельно, или воспользоваться сопоставлением с образцом с помощью [match]match, о котором вы узнаете в следующем разделе. Пока мы еще недостаточно знаем Rust, чтобы реализовывать операции, но мы научимся делать это в разделе [traits]traits.