Enum Sizes

Rust enums are packed tightly, taking constraints due to alignment into account:

  1. use std::mem::{align_of, size_of};
  2. macro_rules! dbg_size {
  3. ($t:ty) => {
  4. println!("{}: size {} bytes, align: {} bytes",
  5. stringify!($t), size_of::<$t>(), align_of::<$t>());
  6. };
  7. }
  8. enum Foo {
  9. A,
  10. B,
  11. }
  12. #[repr(u32)]
  13. enum Bar {
  14. A, // 0
  15. B = 10000,
  16. C, // 10001
  17. }
  18. fn main() {
  19. dbg_size!(Foo);
  20. dbg_size!(Bar);
  21. dbg_size!(bool);
  22. dbg_size!(Option<bool>);
  23. dbg_size!(&i32);
  24. dbg_size!(Option<&i32>);
  25. }

Key Points:

  • Internally Rust is using a field (discriminant) to keep track of the enum variant.
  • Bar enum demonstrates that there is a way to control the discriminant value and type. If repr is removed, the discriminant type takes 2 bytes, becuase 10001 fits 2 bytes.
  • As a niche optimization an enum discriminant is merged with the pointer so that Option<&Foo> is the same size as &Foo.
  • Option<bool> is another example of tight packing.
  • For some types, Rust guarantees that size_of::<T>() equals size_of::<Option<T>>().
  • Zero-sized types allow for efficient implementation of HashSet using HashMap with () as the value.

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.

  1. use std::mem::transmute;
  2. macro_rules! dbg_bits {
  3. ($e:expr, $bit_type:ty) => {
  4. println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e));
  5. };
  6. }
  7. fn main() {
  8. // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise
  9. // representation of types.
  10. unsafe {
  11. println!("Bitwise representation of bool");
  12. dbg_bits!(false, u8);
  13. dbg_bits!(true, u8);
  14. println!("Bitwise representation of Option<bool>");
  15. dbg_bits!(None::<bool>, u8);
  16. dbg_bits!(Some(false), u8);
  17. dbg_bits!(Some(true), u8);
  18. println!("Bitwise representation of Option<Option<bool>>");
  19. dbg_bits!(Some(Some(false)), u8);
  20. dbg_bits!(Some(Some(true)), u8);
  21. dbg_bits!(Some(None::<bool>), u8);
  22. dbg_bits!(None::<Option<bool>>, u8);
  23. println!("Bitwise representation of Option<&i32>");
  24. dbg_bits!(None::<&i32>, usize);
  25. dbg_bits!(Some(&0i32), usize);
  26. }
  27. }

More complex example if you want to discuss what happens when we chain more than 256 Options together.

  1. #![recursion_limit = "1000"]
  2. use std::mem::transmute;
  3. macro_rules! dbg_bits {
  4. ($e:expr, $bit_type:ty) => {
  5. println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e));
  6. };
  7. }
  8. // Macro to wrap a value in 2^n Some() where n is the number of "@" signs.
  9. // Increasing the recursion limit is required to evaluate this macro.
  10. macro_rules! many_options {
  11. ($value:expr) => { Some($value) };
  12. ($value:expr, @) => {
  13. Some(Some($value))
  14. };
  15. ($value:expr, @ $($more:tt)+) => {
  16. many_options!(many_options!($value, $($more)+), $($more)+)
  17. };
  18. }
  19. fn main() {
  20. // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise
  21. // representation of types.
  22. unsafe {
  23. assert_eq!(many_options!(false), Some(false));
  24. assert_eq!(many_options!(false, @), Some(Some(false)));
  25. assert_eq!(many_options!(false, @@), Some(Some(Some(Some(false)))));
  26. println!("Bitwise representation of a chain of 128 Option's.");
  27. dbg_bits!(many_options!(false, @@@@@@@), u8);
  28. dbg_bits!(many_options!(true, @@@@@@@), u8);
  29. println!("Bitwise representation of a chain of 256 Option's.");
  30. dbg_bits!(many_options!(false, @@@@@@@@), u16);
  31. dbg_bits!(many_options!(true, @@@@@@@@), u16);
  32. println!("Bitwise representation of a chain of 257 Option's.");
  33. dbg_bits!(many_options!(Some(false), @@@@@@@@), u16);
  34. dbg_bits!(many_options!(Some(true), @@@@@@@@), u16);
  35. dbg_bits!(many_options!(None::<bool>, @@@@@@@@), u16);
  36. }
  37. }