Перечисления
Ключевое слово enum позволяет создать тип, у которого есть несколько разных вариантов:
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
#[derive(Debug)]
enum Direction {
Left,
Right,
}
#[derive(Debug)]
enum PlayerMove {
Pass, // Простой вариант
Run(Direction), // Вариант-кортеж
Teleport { x: u32, y: u32 }, // Вариант-структура
}
fn main() {
let dir = Direction::Left;
let player_move: PlayerMove = PlayerMove::Run(dir);
println!("On this turn: {player_move:?}");
}
Ключевые моменты:
- Перечисления позволяют объединить набор значений в один тип.
Direction— это тип с вариантами. УDirectionесть два значения:Direction::LeftиDirection::Right.PlayerMove— это тип с тремя вариантами. Помимо данных вариантов, Rust хранит дискриминант, чтобы во время выполнения знать, какой вариант находится в значенииPlayerMove.- Сейчас хорошее время сравнить структуры и перечисления:
- И там, и там можно иметь простую версию без полей (структура-единица) или версию с полями разных типов (данные вариантов).
- Можно даже реализовать разные варианты перечисления отдельными структурами, но тогда они не будут одним и тем же типом, как если бы все они были определены в одном перечислении.
- Rust использует минимум памяти для хранения дискриминанта.
-
При необходимости он хранит целое минимально необходимого размера.
-
Если допустимые значения вариантов не покрывают все битовые шаблоны, Rust использует недопустимые битовые шаблоны для кодирования дискриминанта (это «оптимизация ниш», niche optimization). Например,
Option<&u8>хранит либо указатель на целое, либоNULLдля вариантаNone. -
При необходимости можно управлять дискриминантом (например, для совместимости с C):
// Copyright 2023 Google LLC // SPDX-License-Identifier: Apache-2.0 #[repr(u32)] enum Bar { A, // 0 B = 10000, C, // 10001 } fn main() { println!("A: {}", Bar::A as u32); println!("B: {}", Bar::B as u32); println!("C: {}", Bar::C as u32); }Без
reprтип дискриминанта занимает 2 байта, так как 10001 помещается в 2 байта.
-
Для дальнейшего изучения
У Rust есть несколько оптимизаций, которые позволяют перечислениям занимать меньше места.
-
Оптимизация нулевого указателя: для некоторых типов Rust гарантирует, что
size_of::<T>()равенsize_of::<Option<T>>().Пример кода, если вы хотите показать, как побитовое представление может выглядеть на практике. Важно отметить, что компилятор не даёт никаких гарантий относительно этого представления, поэтому такой код полностью небезопасен.
// Copyright 2023 Google LLC // SPDX-License-Identifier: Apache-2.0 use std::mem::transmute; macro_rules! dbg_bits { ($e:expr, $bit_type:ty) => { println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e)); }; } fn main() { unsafe { println!("bool:"); dbg_bits!(false, u8); dbg_bits!(true, u8); println!("Option<bool>:"); dbg_bits!(None::<bool>, u8); dbg_bits!(Some(false), u8); dbg_bits!(Some(true), u8); println!("Option<Option<bool>>:"); dbg_bits!(Some(Some(false)), u8); dbg_bits!(Some(Some(true)), u8); dbg_bits!(Some(None::<bool>), u8); dbg_bits!(None::<Option<bool>>, u8); println!("Option<&i32>:"); dbg_bits!(None::<&i32>, usize); dbg_bits!(Some(&0i32), usize); } }