Summary

Leave structs

構造、構造体
with unspecified
特定されていない
layout by default like enums, for optimisation purposes. Use something like #[repr(C)] to expose C compatible layout.

Motivation

The members of a struct

構造、構造体
are always laid in memory in the order
順序
in which they were specified,
特定する、指定する、規定する
e.g.

#![allow(unused)] fn main() { struct A { x: u8, y: u64, z: i8, w: i64, } }

will put the u8 first in memory, then the u64, the i8 and lastly the i64. Due to the alignment

揃えること
requirements of various
さまざまな
types padding is often required to ensure
保証する
the members start at an appropriately aligned
揃える
byte. Hence the above struct
構造、構造体
is not 1 + 8 + 1 + 8 == 18 bytes, but rather 1 + 7 + 8 + 1 + 7 + 8 == 32 bytes, since it is laid out like

#![allow(unused)] fn main() { #[packed] // no automatically inserted padding struct AFull { x: u8, _padding1: [u8, .. 7], y: u64, z: i8, _padding2: [u8, .. 7], w: i64 } }

If the fields were reordered to

#![allow(unused)] fn main() { struct B { y: u64, w: i64, x: u8, i: i8 } }

then the struct

構造、構造体
is (strictly) only 18 bytes (but the alignment
揃えること
requirements of u64 forces it to take
とる
up 24).

Having an undefined layout does allow

許可する、可能にする
for possible security improvements, like randomising struct
構造、構造体
fields, but this can trivially be done with a syntax
文法
extension that can be attached to a struct
構造、構造体
to reorder the fields in the AST itself. That said, there may be benefits from being able to randomise all structs
構造、構造体
in a program automatically/for testing, effectively fuzzing code (especially unsafe code).

Notably, Rust's enums already have undefined layout, and provide the #[repr] attribute to control

制御する
layout more precisely
正確に
(specifically, selecting the size of the discriminant).

Drawbacks

Forgetting to add #[repr(C)] for a struct

構造、構造体
intended for FFI use can cause
起こす
surprising bugs and crashes. There is already a lint for FFI use of enums without a #[repr(...)] attribute, so this can be extended
拡張する
to include structs.
構造、構造体

Having an unspecified

特定されていない
(or otherwise
さもなければ
non-C-compatible) layout by default makes interfacing with C slightly harder. A particularly bad case is passing to C a struct
構造、構造体
from an upstream library that doesn't have a repr(C) attribute. This situation seems relatively similar
似ている、同様の
to one where an upstream library type is missing an implementation
実装
of a core trait e.g. Hash if one wishes to use it as a hashmap key.

It is slightly better if structs

構造、構造体
had a specified-but-C-incompatible layout, and one has control
制御する
over the C interface, because then one can manually arrange the fields in the C definition
定義
to match
一致する、マッチさせる
the Rust order.
順序

That said, this scenario requires:

  • Needing to pass a Rust struct
    構造、構造体
    into C/FFI code, where that FFI code actually needs to use things from the struct,
    構造、構造体
    rather than just pass it through, e.g., back into a Rust callback.
  • The Rust struct
    構造、構造体
    is defined
    定義する
    upstream & out of your control,
    制御する
    and not intended for use with C code.
  • The C/FFI code is designed
    設計(する)
    by someone other than that vendor, or otherwise
    さもなければ
    not designed
    設計(する)
    for use with the Rust struct
    構造、構造体
    (or else it is a bug in the vendor's library that the Rust struct
    構造、構造体
    can't be sanely passed to C).

Detailed design
設計(する)

A struct

構造、構造体
declaration
宣言
like

#![allow(unused)] fn main() { struct Foo { x: T, y: U, ... } }

has no fixed layout, that is, a compiler can choose whichever order

順序
of fields it prefers.

A fixed layout can be selected with the #[repr] attribute

#![allow(unused)] fn main() { #[repr(C)] struct Foo { x: T, y: U, ... } }

This will force a struct

構造、構造体
to be laid out like the equivalent
等価
definition
定義
in C.

There would be a lint for the use of non-repr(C) structs

構造、構造体
in related FFI definitions,
定義
for example:

#![allow(unused)] fn main() { struct UnspecifiedLayout { // ... } #[repr(C)] struct CLayout { // ... } extern { fn foo(x: UnspecifiedLayout); // warning: use of non-FFI-safe struct in extern declaration fn bar(x: CLayout); // no warning } extern "C" fn foo(x: UnspecifiedLayout) { } // warning: use of non-FFI-safe struct in function with C abi. }

Alternatives
代わりのもの、選択肢

  • Have non-C layouts opt-in, via #[repr(smallest)] and #[repr(random)] (or similar).
  • Have layout defined,
    定義する
    but not declaration
    宣言
    order
    順序
    (like Java(?)), for example, from largest field to smallest, so u8 fields get placed last, and [u8, .. 1000000] fields get placed first. The #[repr] attributes would still allow
    許可する、可能にする
    for selecting declaration-order layout.

Unresolved questions

  • How does this interact with binary
    2進数
    compatibility
    互換性
    of dynamic
    動的
    libraries?
  • How does this interact with DST, where some fields have to be at the end of a struct? (Just always lay-out unsized fields last? (i.e. after monomorphisation if a field was originally marked Sized? then it needs to be last).)