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>(); }
- Consulte a Referência do Rust.
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 igualasize_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
Option
s 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); } }