Перечисления или тип enum представляет набор вариантов или состояний, в которых может пребывать переменная перечисления. И в зависимости от текущего состояния мы можем выполнить те или иные действия.
Формальное определение перечисления:
enum название_перечисления
{
вариант1,
вариант2,
...........
вариантN
}
После названия перечисления в фигурных скобках через запятую перечисляются варианты перечисления.
Например, определим перечисление, которое назовем OperationType и которое будет представлять арифметические операции:
enum OperationType{
Add, // сложение
Subtract, // вычитание
Multiply, // умножение
Divide // деление
}
Для обращения к варианту перечисления указывается название перечисления и через два двоеточия его вариант:
название_перечисления::вариант
Например, мы можем определить переменную перечисления:
let op = OperationType::Multiply;
В данном случае переменная op представляет вариант OperationType::Multiply.
Как было сказано в начале, перечисления удобны для определения состояний. И в зависимости от текущего состояния выполняется то или иное действие. Например:
fn main(){
// определяем переменную перечисления OperationType
let op = OperationType::Multiply;
match op
{
OperationType::Add => println!("Сложение"),
OperationType::Subtract => println!("Вычитание"),
OperationType::Multiply => println!("Умножение"),
OperationType::Divide => println!("Деление")
}
}
enum OperationType{
Add, // сложение
Subtract, // вычитание
Multiply, // умножение
Divide // деление
}
Здесь в конструкции match проверяется состояние переменной op. В зависимости от значения этой переменной выводится то или иное сообщение на консоль.
Поскольку эта переменная равна OperationType::Multiply, то будет выполняться выражение:
OperationType::Multiply => println!("Умножение"),
Если в будущем потребуется изменить значение переменной перечисления, то такая переменная должна быть определена с помощью ключевого слова mut. Например, применим перечисление OperationType для выполнения арифметических операций:
fn main(){
let a = 10;
let b = 5;
let mut op = OperationType::Add;
let mut result = get_result(a, b, op);
println!("result = {}", result); // result = 15
// меняем операцию
op = OperationType::Subtract;
result = get_result(a, b, op); // result = 5
println!("result = {}", result);
// передаем еще одно значение
result = get_result(a, b, OperationType::Divide);
println!("result = {}", result); // result = 2
}
fn get_result(x: i32, y: i32, op: OperationType) -> i32{
match op
{
OperationType::Add => x + y,
OperationType::Subtract => x - y,
OperationType::Multiply => x * y,
OperationType::Divide => x / y
}
}
enum OperationType{
Add, // сложение
Subtract, // вычитание
Multiply, // умножение
Divide // деление
}
В данном случае все арифметические операции вынесены в отдельную функцию get_result(), которая в качестве параметров принимает
два числа (над которыми выполняется операция) и значение перечисления OperationType, которое указывает, какую именно операцию надо выполнять.
Результат операции возвращается функцией.
В функции main вызывается функция get_result(), в которую передаются различные типы операций, и соответственно функция будет возвращать различный результат:
result = 15 result = 5 result = 2
К вариантам перечисления можно прикрепить некоторые значения. Выше варианты перечисления OperationType не имели никаких данных, но мы можем их определить. Например:
enum DayTime{
Morning(String),
Evening(String)
}
Здесь с обоими вариантами перечисления DayTime ассоциирован кортеж с одним значением типа String, то есть по сути варианты перечисления могут хранить некоторую строку. А это значит, что при создании объекта этого перечисления нам надо передать ему кортеж с одной строкой:
let dt = DayTime::Morning("Доброе утро".to_string());
Значение передается в скобках после названия варианта. Поскольку в данном случае вариант DayTime::Morning представляет тип String,
то переданная строка преобразуется с помощью метода to_string() к типу String.
При этом с разными вариантами перечисления может быть ассоциировано различное количество значений. Далее будет рассмотрен синтасис шаблонов, который позволяет получить и использовать значения, хранимые в вариантах перечисления, а пока вкратце посмотрим еще одни пример использования значений в перечислениях:
fn main(){
let op1 = OperationType::Sum(5, 6);
let op2 = OperationType::Negation(7);
let op3 = OperationType::Zero;
print_op(op1);
print_op(op2);
print_op(op3);
}
fn print_op(op: OperationType){
match op {
OperationType::Sum(x, y) => println!("Сложение {x} и {y}"),
OperationType::Negation(n) => println!("Умножение на -1 числа {n}"),
OperationType::Zero => println!("Ноль")
}
}
enum OperationType{
Sum(i32, i32), // сложение
Negation(i32), // умножение на -1
Zero, // просто возвращаем 0
}
Здесь перечисление OperationType имеет три варианта. Первый вариант - Sum, который представляет условное сложение, принимает кортеж с двумя числами i32. Вариант Negation принимает одно число, а с вариантом Zero вообще не ассоциировано никаких значений.
В функции print_op выводим информацию о варианте перечисления на консоль. Для получения ассоциированных с вариантом данных в конструкции match указывается вариант, а в скобках определяется кортеж с переменными для каждого значения:
OperationType::Sum(x, y) => println!("Сложение {x} {y}")
Далее мы подробнее рассмотрим подобный синтаксис. В итоге программа даст нам следующий консольный вывод:
Сложение 5 и 6 Умножение на -1 числа 7 Ноль
Мы также можем определять значения вариантов перечислений в виде структур:
fn main(){
let user1 = Users::Person("Tom".to_string());
let user2 = Users::Employee{name: "Bob".to_string(), company: "Rust Foundation".to_string()};
print_user(&user1);
print_user(&user2);
}
fn print_user(user: &Users){
match user {
Users::Person(name) => println!("Person {name}"),
Users::Employee{name: emp_name, company: emp_company} =>
println!("Employee {emp_name} ({emp_company})")
}
}
enum Users{
Person(String),
Employee {name:String, company:String}
}
Здесь в перечислении Users есть два варианта: Person(простой человек) и Employee (работник). Если с Person ассоциирован кортеж с одинм значением, то с Employee - структура с двумя полями - name и company. Соответственно при определении значения этого варианта надо определить структуру:
let user2 = Users::Employee{name: "Bob".to_string(), company: "Rust Foundation".to_string()};
Обратите внимание, что при передаче значения перечисления в функцию действуют те же самые правила передачи владения. Поэтому в примере выше, чтобы передачи владения не произошло, применяется передача по ссылке.