Option 的大小

Wu Yu Wei published on
3 min, 471 words

在 Rust 中預設是不能使用 Null 指標/引用的,而當時的設計者 Tony Hoare 也說了這是造成數十億損失的錯誤設計。所以通常我們會使用 Option 型態來表示可能會有 Null 值的型態。這麼做帶來很多好處,所有型態預設都是沒有 Null 的,而 Option<T> 剛好也形成一種 monadic structure。你可以在不和原生型態衝突的狀況下,自己定義他的運算行爲。

但是對於資源吃緊的裝置來說,我們就會在意型態的大小了。我們很清楚的知道 u64 就是有 64 bits 所以會佔 8 bytes,但是 Option<u64> 的話就需要多一個 bit 來儲存 None,所以一開始想的話我們應該會覺得它應該會佔 9 bytes,但結果卻是 16 bytes。

playground

assert_eq!(9, std::mem::size_of::<Option<u64>>());
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `9`,
 right: `16`', src/main.rs:2:5

這是因爲像是在 64-bit 的平台上的 CPU 都是 word-oriented,所有的形態預設都需要 align 爲 8 bytes 的界限。這樣就造成了一些效能的損失,當然這些在平常的應用開發不太重要,編譯器甚至能優化掉,但如果是要寫非常注重空間或記憶體消耗的話(像是寫 file system, kernel module 或 嵌入式系統),可能就需要一些考慮了。爲此 std::num 提供了沒有零的原生型態像是 NonZeroU8, NonZeroUsize 等等。因爲他們的 0 是用不到的,所以作爲 Option 型態的話,就可以剛好給 None 使用。這樣一來大小絕對就和原本的型態一樣了。

playground

assert_eq!(8, std::mem::size_of::<Option<std::num::NonZeroU64>>());