Tamanhos de Enum

Enums, em Rust, são agrupados de maneira compacta, levando em consideração restrições devido ao alinhamento:

use std::any::type_name;
use std::mem::{align_of, size_of};

fn dbg_size<T>() {
    println!("{}: tamanho {} bytes, alinhamento: {} bytes",
        type_name::<T>(), size_of::<T>(), align_of::<T>());
}

enum Foo {
    A,
    B,
}

fn main() {
    dbg_size::<Foo>();
}

Pontos Chave:

  • Internamente Rust utiliza um campo (discriminante) para saber qual a variante da enum.

  • É possível controlar a discriminante se necessário (e.g., para compatibilidade com 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);
    }

    Sem repr, o tipo da discriminante usa 2 bytes, porque 10001 cabe em 2 bytes.

  • Tente outros tipos como

    • dbg_size!(bool): tamanho 1 bytes, alinhamento: 1 bytes,
    • dbg_size!(Option<bool>): tamanho 1 bytes, alinhamento: 1 bytes (otimização de nicho, seja abaixo),
    • dbg_size!(&i32): tamanho 8 bytes, alinhamento: 8 bytes (em uma máquina de 64-bits),
    • dbg_size!(Option<&i32>): tamanho 8 bytes, alinhamento: 8 bytes (otimização de ponteiro nulo, veja abaixo).
  • Otimização de nicho: Rust vai mesclar padrões de bits não utilizados na discriminante da enum.

  • Otimização de ponteiro nulo: para alguns tipos, o Rust garante que size_of::<T>() se iguala size_of::<Option<T>>().

    Código de exemplo caso queira mostrar como a representação em bits pode ser na prática. É importante apontar que o compilador não oferece nenhuma garantia a respeito dessa representação, portanto isso é completamente 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!("Representação em bits de booleano");
            dbg_bits!(false, u8);
            dbg_bits!(true, u8);
    
            println!("Representação em bits de Option<bool>");
            dbg_bits!(None::<bool>, u8);
            dbg_bits!(Some(false), u8);
            dbg_bits!(Some(true), u8);
    
            println!("Representação em bits de 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!("Representação em bits de Option<&i32>");
            dbg_bits!(None::<&i32>, usize);
            dbg_bits!(Some(&0i32), usize);
        }
    }

    Exemplo mais complexo caso queira demonstrar o que acontece ao encadear mais de 256 Options de uma vez.

    #![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!("Representação em bits de uma sequência de 128 Option's.");
            dbg_bits!(many_options!(false, @@@@@@@), u8);
            dbg_bits!(many_options!(true, @@@@@@@), u8);
    
            println!("Representação em bits de uma sequência de 256 Option's.");
            dbg_bits!(many_options!(false, @@@@@@@@), u16);
            dbg_bits!(many_options!(true, @@@@@@@@), u16);
    
            println!("Representação em bits de uma sequência de 257 Option's.");
            dbg_bits!(many_options!(Some(false), @@@@@@@@), u16);
            dbg_bits!(many_options!(Some(true), @@@@@@@@), u16);
            dbg_bits!(many_options!(None::<bool>, @@@@@@@@), u16);
        }
    }