Tamaños de Enum
Las enumeraciones de Rust son densamente empaquetadas, ya que tienen en cuenta las restricciones debidas a la alineación:
use std::any::type_name; use std::mem::{align_of, size_of}; fn dbg_size<T>() { println!("{}: size {} bytes, align: {} bytes", type_name::<T>(), size_of::<T>(), align_of::<T>()); } enum Foo { A, B, } fn main() { dbg_size::<Foo>(); }
- Consulta el libro Rust Reference.
Puntos Clave:
-
Rust utiliza un campo (discriminante) de forma interna para hacer un seguimiento de la variante de enumeración.
-
Puedes controlar el discriminante si es necesario (por ejemplo, para asegurar la compatibilidad con 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); }
Sin
repr
, el tipo discriminante ocupa 2 bytes, debido a que 10001 se cabe en 2 bytes. -
Prueba otros tipos, como
dbg_size!(bool)
: tamaño de 1 byte, alineación de 1 byte,dbg_size!(Option<bool>)
: tamaño de 1 byte, alineación de 1 byte (optimización de nichos, consulta más abajo),dbg_size!(&i32)
: tamaño de 8 bytes, alineación de 8 bytes (en una máquina de 64 bits),dbg_size!(Option<&i32>)
: tamaño de 8 bytes, alineación de 8 bytes (optimización del puntero nulo, consulta más abajo).
-
Optimización de nichos: Rust combina los patrones de bits no utilizados para el discriminante de enumeración.
-
Optimización de puntero nulo: para algunos tipos, Rust asegura que
size_of::<T>()
es igual asize_of::<Option<T> >()
.Fragmento de código de ejemplo si quieres mostrar cómo puede ser la representación bit a bit en la práctica. Es importante tener en cuenta que el compilador no ofrece garantías con respecto a esta representación, por lo tanto es totalmente inseguro.
use std::mem::transmute; macro_rules! dbg_bits { ($e:expr, $bit_type:ty) => { println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e)); }; } fn main() { // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise // representation of types. unsafe { println!("Bitwise representation of bool"); dbg_bits!(false, u8); dbg_bits!(true, u8); println!("Bitwise representation of Option<bool>"); dbg_bits!(None::<bool>, u8); dbg_bits!(Some(false), u8); dbg_bits!(Some(true), u8); println!("Bitwise representation of 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!("Bitwise representation of Option<&i32>"); dbg_bits!(None::<&i32>, usize); dbg_bits!(Some(&0i32), usize); } }
Ejemplo más complejo si quieres hablar de lo que pasa cuando encadenamos más de 256
Option
.#![recursion_limit = "1000"] use std::mem::transmute; macro_rules! dbg_bits { ($e:expr, $bit_type:ty) => { println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e)); }; } // Macro to wrap a value in 2^n Some() where n is the number of "@" signs. // Increasing the recursion limit is required to evaluate this macro. macro_rules! many_options { ($value:expr) => { Some($value) }; ($value:expr, @) => { Some(Some($value)) }; ($value:expr, @ $($more:tt)+) => { many_options!(many_options!($value, $($more)+), $($more)+) }; } fn main() { // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise // representation of types. unsafe { assert_eq!(many_options!(false), Some(false)); assert_eq!(many_options!(false, @), Some(Some(false))); assert_eq!(many_options!(false, @@), Some(Some(Some(Some(false))))); println!("Bitwise representation of a chain of 128 Option's."); dbg_bits!(many_options!(false, @@@@@@@), u8); dbg_bits!(many_options!(true, @@@@@@@), u8); println!("Bitwise representation of a chain of 256 Option's."); dbg_bits!(many_options!(false, @@@@@@@@), u16); dbg_bits!(many_options!(true, @@@@@@@@), u16); println!("Bitwise representation of a chain of 257 Option's."); dbg_bits!(many_options!(Some(false), @@@@@@@@), u16); dbg_bits!(many_options!(Some(true), @@@@@@@@), u16); dbg_bits!(many_options!(None::<bool>, @@@@@@@@), u16); } }