2025/04/07 22:30 A surprising enum size optimization in the Rust compiler

やっほー、ロボ子! 今日もRustのメモリレイアウトについて勉強するのじゃ!

博士、こんにちは! メモリレイアウト、奥が深くて面白いですよね。今日もよろしくお願いします。

今日はEnumじゃ! Enumって、指定されたバリアントのどれか一つの値を持つ型のことだぞ。

はい、Enumは列挙型のことですね。例えば、`enum Color { Red, Green, Blue }` のように定義できますね。

そうそう! で、Enumのメモリ表現じゃが、これがまた面白いんじゃ。基本的には、最大のペイロードのサイズに、どのバリアントかを示すタグを足したサイズになるぞ。

タグというのは、どのバリアントが格納されているかを示す識別子のことですね。

`Foo`型を例に見てみるのじゃ。`Foo`は整数か文字のバリアントを持っていて、それぞれのペイロードは4バイトじゃ。タグには最低1バイト必要じゃが、アライメントの関係で4バイトになるから、全体のサイズは8バイトになる。

アライメントというのは、メモリ上のアドレスを特定の境界に揃えることですね。4バイトのアライメントだと、アドレスが4の倍数になるように調整されるんでしたっけ。

その通り! そして、このコンピュータはリトルエンディアンだから、最下位バイトが最初に来るぞ。

リトルエンディアンだと、例えば32ビットの整数`0x12345678`は、メモリ上では`78 56 34 12`の順で格納されるんですね。

その通り! `Foo::Int`にはタグ0、`Foo::Char`にはタグ1が割り当てられるんじゃ。

なるほど。タグでどのバリアントかを区別するんですね。

そして、ニッチ最適化! これはEnumのサイズを小さくするテクニックの一つじゃ。バリアントの一つだけがペイロードを持つ場合に使えるぞ。

ニッチ最適化ですか。具体的にはどのような場合に適用されるんですか?

`Option<char>`型が良い例じゃ。`char`は4バイトなのに、`Option<char>`も4バイトで済むんじゃ!

えっ、どうしてですか? `Option`は`Some(char)`か`None`のどちらかですよね。タグが必要なので、`char`の4バイトに加えてタグの分のサイズが必要になるはずでは…?

そこがミソなのじゃ! `char`の有効な値の範囲は4バイトで表現できるすべての値よりも狭いから、コンパイラが無効なビットパターンをニッチとして使うんじゃ。だから、タグなしでEnumの値を表現できるのじゃ!

なるほど! `char`で表現できない値、例えば`0x00110000`を`None`として使うんですね。

そう! そして、ネストされたEnumでは、さらに最適化が進むんじゃ。

ネストされたEnumですか。例えば、次のような場合ですね。
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。