代替メモリレイアウト

Rust では、デフォルトとは異なる、代替のデータレイアウトを指定することができます。

repr(C)

これは最も重要な repr です。意味はとても単純で、「C がやるようにやれ」です。 フィールドの順序、サイズ、アラインメントが、C や C++ に期待するのと全く同じになります。 FFI 境界を超えるであろう型は、すべて repr(C) になるべきです。 C はプログラミング界の共通言語なのですから。 また、値を別の型として再解釈する、といった複雑なトリックをやる場合にも repr(C) は必須です。

しかし、Rust の風変わりなデータレイアウト機能との相互作用も忘れてはいけません。 「FFI のため」と「データレイアウトのため」という二つの目的があるため、 FFI 境界を超えることが無意味または問題になるような型にも repr(C) は適用されます。

  • ZST のサイズはやはり 0 になります。これは C の標準的な挙動ではないし、C++ の挙動 (空の型も 1 byte を消費します)とは明確に異なります。

  • DST, タプル, タグ付き共用体という概念は C には存在しないため、FFI では安全に使えません。

  • repr(C) を適用した状況では、タプルは構造体と似ています。構造体との違いは、フィールドに名前がないことだけです。

  • 型に drop flags が付いていても、その型は追加されます。

  • enum については、repr(u*) (次のセクションで説明します)と同等です。選んだサイズが、対象となるプラットフォームの C ABI でのデフォルトの enum のサイズとなります。C での enum のデータ表現は実装依存なので、これはベストの推測でしかないことに注意してください。とくに、対象の C コードが特定のフラグつきでコンパイルされた場合に、正しく動かないかもしれません。

repr(u8), repr(u16), repr(u32), repr(u64)

これらは、enum を C っぽくレイアウトするように指示します。 enum の要素が指定した整数をオーバーフローする場合、コンパイルエラーとなります。 オーバーフローする値を 0 に設定するよう Rust に指定することもできますが、 2 つの異なる enum 要素が同じ値を取ることはできません。

C っぽくない enum (訳注:要素がパラメータをとるような enum)に repr(u*) を適用すると、 ヌルポインタ最適化のようなある種の最適化ができなくなります。

この repr を構造体につかっても効果はありません。

repr(packed)

repr(packed) を使うと Rust はパディングを一切取り除き、すべてをバイト単位にアラインします。 メモリ使用量は改善しますが、悪い副作用を引き起こす可能性が高いです。

特にほとんどのアーキテクチャは、値がアラインされていることを強く望んでいます。 つまりアラインされていないデータの読み込みにはペナルティがある(x86)かもしれませんし、 失敗する(いくつかの ARM チップ)かもしれません。 パックされたフィールドを直接読んだり書いたりするという単純なケースでは、 コンパイラがシフトやマスクを駆使してアラインメントの問題を隠してくれるかもしれません。 しかし、パックされたフィールドへの参照を扱う場合には、アラインされてない読み込みを避けるような コードをコンパイラが生成することは期待できないでしょう。

Rust 1.0 時点では、これは未定義な挙動です。

repr(packed) は気軽に使えるものではありません。 極端な要求に応えようとしているのでない限り、使うべきではありません。

この repr は repr(C)repr(rust) の修飾子として使えます。

関連キーワード:  repr, 代替, メモリレイアウト, サイズ, メモリ, フィールド, データレイアウト, アライン, ライフタイム, 適用