src/user-defined-types/enums.md
The enum keyword allows the creation of a type which has a few different
variants:
# // Copyright 2023 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
#[derive(Debug)]
enum Direction {
Left,
Right,
}
#[derive(Debug)]
enum PlayerMove {
Pass, // Simple variant
Run(Direction), // Tuple variant
Teleport { x: u32, y: u32 }, // Struct variant
}
fn main() {
let dir = Direction::Left;
let player_move: PlayerMove = PlayerMove::Run(dir);
println!("On this turn: {player_move:?}");
}
Key Points:
Direction is a type with variants. There are two values of Direction:
Direction::Left and Direction::Right.PlayerMove is a type with three variants. In addition to the payloads, Rust
will store a discriminant so that it knows at runtime which variant is in a
PlayerMove value.If necessary, it stores an integer of the smallest required size
If the allowed variant values do not cover all bit patterns, it will use
invalid bit patterns to encode the discriminant (the "niche optimization").
For example, Option<&u8> stores either a pointer to an integer or NULL
for the None variant.
You can control the discriminant if needed (e.g., for compatibility with C):
<!-- mdbook-xgettext: skip --># // 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);
}
Without repr, the discriminant type takes 2 bytes, because 10001 fits 2
bytes.
Rust has several optimizations it can employ to make enums take up less space.
Null pointer optimization: For
some types, Rust
guarantees that size_of::<T>() equals size_of::<Option<T>>().
Example code if you want to show how the bitwise representation may look like in practice. It's important to note that the compiler provides no guarantees regarding this representation, therefore this is totally unsafe.
<!-- mdbook-xgettext: skip --># // 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);
}
}