% Перечисления
В Rust перечисление (enum
) — это тип данных, который представляет собой один
из нескольких возможных вариантов. Каждый вариант в перечислении может быть
также связан с другими данными:
#![allow(unused)] fn main() { enum Message { Quit, ChangeColor(i32, i32, i32), Move { x: i32, y: i32 }, Write(String), } }
Синтаксис для объявления вариантов схож с синтаксисом для объявления структур: у вас могут быть варианты без данных (как unit-подобные структуры), варианты с именованными данными и варианты с безымянными данными (подобно кортежным структурам). Варианты перечисления имеют один и тот же тип, и в отличии от структур не являются определением отдельных типов. Значение перечисления может соответствовать любому из вариантов. Из-за этого перечисления иногда называют тип-сумма (sum-type): множество возможных значений перечисления — это сумма множеств возможных значений каждого варианта.
Мы используем синтаксис ::
чтобы использовать имя каждого из вариантов. Их
область видимости ограничена именем самого перечисления. Это позволяет
использовать оба варианта из примера ниже совместно:
#![allow(unused)] fn main() { enum Message { Move { x: i32, y: i32 }, } let x: Message = Message::Move { x: 3, y: 4 }; enum BoardGameTurn { Move { squares: i32 }, Pass, } let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 }; }
Оба варианта имеют одинаковое имя Move
, но поскольку область видимости
каждого из них ограничена именем соответствующего перечисления, они могут быть
использованы без конфликтов.
Значение перечисления, в дополнение к любым данным, которые связаны с ним, содержит информацию о том, какой именно это вариант. Это иногда называют размеченное объединение (tagged union), поскольку данные включают в себя метку, обозначающую что это за тип.
fn process_color_change(msg: Message) {
let Message::ChangeColor(r, g, b) = msg; // ошибка времени компиляции
}
То, что пользовательские типы по умолчанию не поддерживают операции, может
показаться довольно ограниченным. Но это ограничение, которое мы всегда можем
преодолеть. Есть два способа: реализовать операцию самостоятельно, или
воспользоваться сопоставлением с образцом с помощью match
, о котором
вы узнаете в следующем разделе. Пока мы еще недостаточно знаем Rust, чтобы
реализовывать операции, но мы научимся делать это в разделе traits
.
Конструкторы как функции
Конструктор перечисления может быть также использован как обычная функция. Например:
#![allow(unused)] fn main() { enum Message { Write(String), } let m = Message::Write("Hello, world".to_string()); }
тоже самое, что и
#![allow(unused)] fn main() { enum Message { Write(String), } fn foo(x: String) -> Message { Message::Write(x) } let x = foo("Hello, world".to_string()); }
На данный момент это не так уж и полезно для нас, но когда мы перейдем к
замыканиям, мы поговорим о передаче функций в роли аргумента другой
функции. Например, с помощью итераторов мы можем преобразовывать
вектор строк в вектор состоящий из Message::Write
:
#![allow(unused)] fn main() { enum Message { Write(String), } let v = vec!["Hello".to_string(), "World".to_string()]; let v1: Vec<Message> = v.into_iter().map(Message::Write).collect(); }