Перелічувані типи
Ключове слово enum
дозволяє створити тип, який має кілька різних варіантів:
#[derive(Debug)] enum Direction { Left, Right, } #[derive(Debug)] enum PlayerMove { Pass, // Простий варіант Run(Direction), // Варіант кортежу Teleport { x: u32, y: u32 }, // Варіант структури } fn main() { let player_move: PlayerMove = PlayerMove::Run(Direction::Left); println!("На цьому повороті: {player_move:?}"); }
Ключові моменти:
- Переліки дозволяють збирати набір значень під одним типом.
- Напрямок - це тип з варіантами. Існує два значення
Direction
:Direction::Left
таDirection::Right
. PlayerMove
- це тип з трьома варіантами. На додаток до корисного навантаження, Rust зберігатиме дискримінант, щоб під час виконання знати, який варіант є у значенніPlayerMove
.- Це може бути гарний час для порівняння структури та переліки:
- В обох ви можете мати просту версію без полів (структура одиниць) або з різними типами полів (різні варіанти корисного навантаження).
- Ви навіть можете реалізувати різні варіанти переліку окремими структурами, але тоді вони не будуть одного типу, як якщо б всі вони були визначені в переліку.
- Rust використовує мінімальний обсяг пам'яті для зберігання дискримінанта.
-
Якщо потрібно, він зберігає ціле число найменшого необхідного розміру
-
Якщо допустимі значення варіантів не покривають усіх бітових шаблонів, для кодування дискримінанта буде використано неприпустимі бітові шаблони ("нішева оптимізація"). Наприклад,
Option<&u8>
зберігає або вказівник на ціле число, абоNULL
для варіантаNone
. -
За потреби можна керувати дискримінантом (наприклад, для сумісності з C):
#[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>>()
.Приклад коду, якщо ви хочете показати, як може виглядати побітове представлення на практиці. Важливо зазначити, що компілятор не надає жодних гарантій щодо цього представлення, тому це абсолютно небезпечно.
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); } }