En Rust, el compilador intenta colocar los datos de manera eficiente en la memoria, utilizando conocimientos sobre alineación y las posibilidades de layout de estructuras y enums. Esta cuestión es especialmente relevante en desarrollo a bajo nivel y de sistemas, donde un tamaño excesivo del tipo puede llevar a un desperdicio considerable de memoria.
La alineación automática de estructuras es una característica de la mayoría de los lenguajes, sin embargo, en Rust el compilador proporciona garantías estrictas sobre el layout (permitiendo optimización), y en el caso de enum implementa un almacenamiento compacto al unir la memoria para todas las variantes, considerando el tamaño máximo).
El orden y los tipos de campos en una estructura o enum afectan el tamaño final del tipo debido a las particularidades de alineación. Un orden incorrecto aumenta el "padding" — bytes no utilizados. En enum con datos asociados, la variante máxima determina el tamaño, y algunas construcciones pueden hacer que el enum sea inesperadamente voluminoso.
Es crucial indicar correctamente el orden de los campos y elegir tipos, analizar el tamaño de los datos a través de std::mem::size_of. Para enums, hay que tener cuidado con las estructuras anidadas y punteros.
Ejemplo de código:
struct Bad { a: u8, // ocupa 1 byte + 7 bytes de padding b: u64, // ocupa 8 bytes } struct Good { b: u64, // 8 bytes, alineación desde el inicio a: u8, // 1 byte + 7 bytes de padding al final }
Verificación del tamaño:
use std::mem::size_of; println!("{}", size_of::<Bad>()); // 16 bytes println!("{}", size_of::<Good>()); // 16 bytes (pero ahora el padding es al final)
Para enums:
enum Example { Unit, Num(u32), Pair(u64, u8), } println!("{}", size_of::<Example>()); // tamaño — max(tamaño de variantes) + discriminante
Características clave:
Si se cambian los campos u8 y u64 en la estructura, ¿cambiará el tamaño de la estructura?
No, el tamaño total seguirá siendo múltiplo de la alineación del campo máximo, pero el padding se desplazará. Esto es importante si la estructura se incluye en otra estructura o se pasa a través de FFI.
¿Puede un pequeño enum tener un gran tamaño de memoria?
Sí, si al menos una variante contiene un objeto grande o una referencia, el tamaño total del enum corresponderá a la variante "más pesada" más el discriminante.
¿Es igual el layout de las estructuras siempre en todas las plataformas?
No, el layout y la alineación pueden diferir entre arquitecturas. Para un control estricto, se utiliza el atributo repr(C).
#[repr(C)] struct MyFFIStruct { x: u32, y: u8, }
En grandes colecciones, se usa un enum con un Vec anidado, que rara vez se encuentra, pero aumenta el tamaño del enum 10 veces. La memoria se desperdicia.
Pros:
Contras:
El enum se divide en varios enums menos grandes, y los arreglos/colecciones se almacenan por separado o a través de Box para variantes raras, controlando el layout a través de #[repr(C)]. Se verifica el tamaño a través de size_of.
Pros:
Contras: