Tamaños de enumeració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>();
}
Internamente, Rust está utilizando un campo (discriminante) para realizar un seguimiento de la variante de enumeración.
Puede controlar el discriminante si es necesario (por ejemplo, para 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, porque 10001 cabe en 2 bytes.
Pruebe otros tipos como
- dbg_size!(bool): tamaño 1 byte, alineación: 1 byte,
- dbg_size!(Opción<bool>): tamaño 1 bytes, alineación: 1 bytes (optimización de nicho, ver más abajo),
- dbg_size!(&i32): tamaño 8 bytes, alineación: 8 bytes (en una máquina de 64 bits),
- dbg_size!(Opción<&i32>): tamaño 8 bytes, alineación: 8 bytes (optimización de puntero nulo, ver más abajo).
Optimización de nicho: Rust fusionará patrones de bits no utilizados para el discriminante de enumeración.
Optimización de puntero nulo: para algunos tipos, Rust garantiza que size_of::<T>() es igual a size_of::<Option<T>>().
Código de ejemplo si desea mostrar cómo se vería 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 que 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 desea analizar qué sucede cuando encadenamos más de 256 opciones juntas.
#![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);
}
}