Перечисления и Option
Перечисления задают тип через набор вариантов; на их основе построен Option — замена null.
Перечисление (enum) — тип, значение которого — ровно один из заранее перечисленных вариантов, причём каждый вариант может нести свои данные.
Простые перечисления
Enum описывает значение, которое может быть одним из нескольких вариантов. Это надёжнее строковых констант: компилятор знает полный список и не даст забыть ни один.
enum Direction {
North,
South,
East,
West,
}
fn describe(d: Direction) -> &'static str {
match d {
Direction::North => "север",
Direction::South => "юг",
Direction::East => "восток",
Direction::West => "запад",
}
}
fn main() {
println!("{}", describe(Direction::East));
}Вывод:
восток
Варианты с данными
Сила Rust-перечислений в том, что варианты могут нести данные, и у разных вариантов — разные. Один тип Message описывает совершенно разные по форме сообщения.
enum Message {
Quit, // без данных
Move { x: i32, y: i32 }, // именованные поля
Write(String), // одно значение
Color(u8, u8, u8), // три значения
}
fn handle(m: Message) {
match m {
Message::Quit => println!("выход"),
Message::Move { x, y } => println!("шаг в ({x}, {y})"),
Message::Write(text) => println!("текст: {text}"),
Message::Color(r, g, b) => println!("цвет {r},{g},{b}"),
}
}
fn main() {
handle(Message::Move { x: 3, y: 7 });
handle(Message::Write(String::from("привет")));
}Вывод:
шаг в (3, 7) текст: привет
Option — Rust без null
В большинстве языков любое значение может оказаться null, и забытая проверка приводит к падению (NullPointerException и его родня). Создатель null назвал это своей «ошибкой на миллиард долларов». В Rust нет null вообще. Вместо него — перечисление Option<T> из стандартной библиотеки:
// так Option определён в стандартной библиотеке
enum Option<T> {
Some(T), // значение есть, вот оно
None, // значения нет
}Хитрость в том, что отсутствие значения теперь видно в типе. Если функция может ничего не вернуть, её тип — Option<T>, и компилятор заставит вас обработать вариант None. Забыть проверку невозможно.
fn find_even(nums: &[i32]) -> Option<i32> {
for &n in nums {
if n % 2 == 0 {
return Some(n); // нашли — возвращаем Some
}
}
None // не нашли
}
fn main() {
let data = [1, 3, 4, 7];
match find_even(&data) {
Some(n) => println!("первое чётное: {n}"),
None => println!("чётных нет"),
}
}Вывод:
первое чётное: 4
Чтобы достать значение из Option, его нужно «распаковать» — через match, if let или удобные методы. Это значит, что обращение к отсутствующему значению просто не пройдёт компиляцию. Подробно с Option и match работаем в следующем разделе.
Итог
- Enum — тип «один из вариантов»; варианты могут нести разные данные.
Option<T>заменяетnull:Some(значение)илиNone.- Отсутствие значения видно в типе, поэтому компилятор не даёт забыть его обработать.