4 votes

Quelle est la représentation en mémoire de l'enum et de l'Option<enum> ?

Compte tenu de la situation suivante ( aire de jeux ) :

enum A { A = 0, B, C }
enum B { A = 1, B, C }

Comment Rust représente-t-il les types A , Option<A> , B y Option<B> en mémoire ? Comment Rust peut-il avoir ces quatre éléments sous la forme de représentations d'un seul octet ? Il serait logique que Option<B> mais je m'attendais à ce que Option<A> à deux octets.

9voto

kmdreko Points 3321

Clause de non-responsabilité : Il n'y a aucune garantie que le comportement exact observé ici sera toujours vrai. VOUS NE DEVEZ PAS VOUS FIER AUX VALEURS SPÉCIFIQUES. De plus, cette réponse n'est basée que sur des observations et ne concerne que les enums ; d'autres optimisations de la mémoire existent dans les environs &T s, NonNull<T> s, NonZeroU8 (et famille), et les structures imbriquées qui les contiennent.


Enums de base

Si vous avez une énumération simple sans structure imbriquée, le comportement par défaut est que les variantes de l'énumération commencent à zéro et s'incrémentent vers le haut. Ainsi, le premier motif binaire non représentable est utilisé en tant que motif None valeur :

enum Simple { A, B };
println!("{}", unsafe { transmute::<Option<Simple>, u8>(None) });
// prints 2

Si votre simple enum sans structure imbriquée laisse un vide à l'avant, la fonction None sera toujours représentée par le premier motif binaire non représentable après les représentations de la variante enum :

enum GapInFront { A = 1, B };
println!("{}", unsafe { transmute::<Option<GapInFront>, u8>(None) });
// prints 3

Si vous laissez un espace au début et que vous avez une variante à la fin de l'espace binaire, ce n'est qu'à ce moment-là qu'il utilisera tous les zéros en tant qu'élément de l'espace binaire. None valeur :

enum ExtendsToEnd { A = 1, B = 255 };
println!("{}", unsafe { transmute::<Option<ExtendsToEnd>, u8>(None) });
// prints 0

Une chose est à noter, c'est qu'il jamais choisir une représentation entre les variantes pour le None valeur. Même s'il existe de nombreux motifs binaires non représentables, les variantes occupant les limites entraîneront l'utilisation de 2 octets :

enum Full { A = 0, B = 255 };
println!("{:?}", unsafe { transmute::<Option<Full>, [u8; 2]>(None) });
// prints [0, 60], which I believe is undefined behavior 
// since I think the second byte is left uninitialized

Je pense que le compilateur ne garde pas trace de tous les motifs de bits représentables, et qu'il ne conserve que les plages permettant d'effectuer ces vérifications.


Plus d'informations intéressantes.

Si votre enum possède une variante avec une valeur d'enum imbriquée, cela sera également pris en compte :

enum Nested { A, B };
enum Complex { A(Nested), B };
println!("{}", unsafe { transmute::<Option<Complex>, u8>(None) });
// prints 3

Cependant, il semble se casser la figure si deux variantes ont des valeurs, même si leurs motifs de bits ne se chevauchent pas (triste) :

enum Nested1 { A, B };
enum Nested2 { A=2, B };
enum MoreComplex { A(Nested1), B(Nested2) };
println!("{:?}", unsafe { transmute::<Option<MoreComplex>, [u8; 2]>(None) });
// prints [2, 211], again the second byte is left uninitialized

Autre point à souligner, il ne s'agit pas d'un cas particulier avec Option ; si vous définissez votre propre type d'option, le comportement est le même :

enum MyOption<T> { None, Some(T) };
println!("{}", unsafe { transmute::<MyOption<Simple>, u8>(MyOption::None) });
// prints 2

Voir tout cela sur l'aire de jeux .

Voir aussi Quel est l'overhead du type Option de Rust ? .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X