En Rust, le compilateur essaie de positionner efficacement les données en mémoire, en utilisant des connaissances sur l'alignement et les possibilités de layout des structures et des enums. La question est particulièrement pertinente lors du développement bas niveau et système, lorsque la taille excessive d'un type entraîne un gaspillage de mémoire considérable.
L'alignement automatique des structures est une caractéristique de la plupart des langages, mais en Rust, le compilateur fournit de strictes garanties sur le layout (tout en permettant son optimisation), et dans le cas des enums, il met en œuvre un stockage compact en combinant la mémoire pour toutes les variantes, en tenant compte de la taille maximale).
L'ordre et les types de champs dans une structure ou un enum influencent la taille finale du type en raison des particularités d'alignement. Un ordre incorrect augmente le "padding" — des octets non utilisés. Pour un enum avec des données associées, la variante maximale détermine la taille, et certaines constructions peuvent rendre l'enum de manière inattendue encombrant.
Il est nécessaire de spécifier correctement l'ordre des champs et de choisir des types, d'analyser la taille des données via std::mem::size_of. Pour les enums, il faut être plus prudent avec les structures imbriquées et les pointeurs.
Exemple de code :
struct Bad { a: u8, // occupe 1 octet + 7 octets de padding b: u64, // occupe 8 octets } struct Good { b: u64, // 8 octets, alignement dès le début a: u8, // 1 octet + 7 octets de padding à la fin }
Vérification de la taille :
use std::mem::size_of; println!("{}", size_of::<Bad>()); // 16 octets println!("{}", size_of::<Good>()); // 16 octets (mais padding maintenant à la fin)
Pour l'enum :
enum Example { Unit, Num(u32), Pair(u64, u8), } println!("{}", size_of::<Example>()); // taille — max(taille des variantes) + discriminant
Caractéristiques clés :
Si les champs u8 et u64 sont échangés dans la structure, la taille de la structure changera-t-elle ?
Non, la taille totale sera toujours un multiple de l'alignement du champ maximal, mais le padding sera déplacé. Cela est important si la structure est incluse dans une autre structure ou transmise via FFI.
Un petit enum peut-il avoir une grande taille en mémoire ?
Oui, si au moins une variante contient un gros objet ou une référence, la taille totale de l'enum correspondra à la variante la plus "lourde" plus le discriminant.
Le layout des structures est-il toujours identique sur toutes les plateformes ?
Non, le layout et l'alignement peuvent varier entre les architectures. Pour un contrôle strict, on utilise l'attribut repr(C).
#[repr(C)] struct MyFFIStruct { x: u32, y: u8, }
Dans de grandes collections, un enum avec un Vec imbriqué est utilisé, qui est rarement rencontré, mais augmente la taille de l'enum par 10. La mémoire est gaspillée.
Avantages :
Inconvénients :
L'enum est divisé en plusieurs enums moins volumineux, les tableaux/collections sont stockés séparément, soit via Box pour les variantes rares, le layout étant contrôlé via #[repr(C)]. Vérifiez la taille via size_of.
Avantages :
Inconvénients :