Translate

lunes, 21 de agosto de 2023

Enums en Rust parte 3


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