例えば以下のようなenum
を定義した場合、そのサイズは 16 バイトになります。
enum Hoge {
Char(i8),
Short(i16),
Long(i32),
LongLong(i64),
}
let size = mem::size_of::<Hoge>();
assert_eq!(size, 16);
Hoge
のデータ構造がどうなっているか確認します。
let long = Hoge::Long(65536);
let slice = unsafe { mem::transmute::<Hoge, [u8; 16]>(long) };
slice.iter()
.enumerate()
.for_each(|(i, v)| println!("index: {}, value: {:x}", i, v));
$ cargo run
index: 0, value: 2 // tag enum の何番目の識別子か
index: 1, value: 44 // _padding0 ここから
index: 2, value: ed // ↓
index: 3, value: 3 // ここまで
index: 4, value: 0 // data ここから
index: 5, value: 0 // ↓
index: 6, value: 1 // ↓
index: 7, value: 0 // ここまで
index: 8, value: 0 // _padding1 ここから
index: 9, value: 0 // ↓
index: 10, value: 0 // ↓
index: 11, value: 0 // ↓
index: 12, value: 0 // ↓
index: 13, value: 0 // ↓
index: 14, value: 0 // ↓
index: 15, value: 0 // ここまで
つまりenum Hoge
列挙子のデータ構造を仮想的な構造体として表すと、
struct EnumHoge {
tag: u8,
_padding0: [u8; ?],
data: i8 | i16 | i32 | i64,
_padding1: [u8; ?],
}
みたいな感じになっています。
_padding0
の大きさはdata
がメモリアライメントの境界を越えないようにdata
のサイズによって変わります。Char
の_padding
は 0。Short
は 1。Long
は 3。LongLong
なら 7 となります。_padding1
で 16 バイトの残りが埋まります。
ここで新たな疑問が。もし列挙子の数が 256 を越えたらどうなるのか?つまりtag
のu8
がオーバーフローしたらどうなるのか?(現実的にはまずあり得ないと思いますが)
実際にやってみた。
enum Piyo {
LongLong0(i64),
LongLong1(i64),
LongLong2(i64),
LongLong3(i64),
// 省略
LongLong65534(i64),
LongLong65535(i64),
Char(i8),
Short(i16),
Long(i32),
}
VSCode で↑を書いたらメモリ喰いまくって、スワップが 4GiB を超えて PC がフリーズしました
たぶんtag
のサイズがu32
に変わると思います。
話がちょっと脱線しました。メモリサイズ効率が悪いという話に戻しましょう。
例えば↓こんな風にHoge
をVec
に入れると、実際のデータとしてのサイズは 15 バイトなのに padding があるので 64 バイトも使っちゃうよということです。
enum Hoge {
Char(i8), // 1 バイト
Short(i16), // 2 バイト
Long(i32), // 4 バイト
LongLong(i64), // 8 バイト
}
// 1 + 2 + 4 + 8 = 15 バイト!
use Hoge::*;
let v = vec![Char(0), Short(1), Long(2), LongLong(3)];
// v の大きさは 16 * 4 = 64 バイト!
おわり。