% Перечисления
В Rust перечисление (enum
) — это тип данных, который представляет собой один
из нескольких возможных вариантов. Каждый вариант в перечислении может быть
также связан с другими данными:
enum Message {
Quit,
ChangeColor(i32, i32, i32),
Move { x: i32, y: i32 },
Write(String),
}
Синтаксис для объявления вариантов схож с синтаксисом для объявления структур: у вас могут быть варианты без данных (как unit-подобные структуры), варианты с именованными данными и варианты с безымянными данными (подобно кортежным структурам). Варианты перечисления имеют один и тот же тип, и в отличии от структур не являются определением отдельных типов. Значение перечисления может соответствовать любому из вариантов. Из-за этого перечисления иногда называют тип-сумма (sum-type): множество возможных значений перечисления — это сумма множеств возможных значений каждого варианта.
Мы используем синтаксис ::
чтобы использовать имя каждого из вариантов. Их
область видимости ограничена именем самого перечисления. Это позволяет
использовать оба варианта из примера ниже совместно:
# 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
.
Конструкторы как функции
Конструктор перечисления может быть также использован как обычная функция. Например:
# enum Message {
# Write(String),
# }
let m = Message::Write("Hello, world".to_string());
тоже самое, что и
# enum Message {
# Write(String),
# }
fn foo(x: String) -> Message {
Message::Write(x)
}
let x = foo("Hello, world".to_string());
На данный момент это не так уж и полезно для нас, но когда мы перейдем к
замыканиям, мы поговорим о передаче функций в роли аргумента другой
функции. Например, с помощью итераторов мы можем преобразовывать
вектор строк в вектор состоящий из Message::Write
:
# enum Message {
# Write(String),
# }
let v = vec!["Hello".to_string(), "World".to_string()];
let v1: Vec<Message> = v.into_iter().map(Message::Write).collect();