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>();
}

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 a size_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);
        }
    }