Struct Types
A struct type is a composite type consisting of named fields.
A struct is defined using the struct keyword:
struct_def = "struct" IDENT "{" [ struct_fields ] "}" ;
struct_fields = struct_field { "," struct_field } [ "," ] ;
struct_field = IDENT ":" type ;
struct Point {
x: i32,
y: i32,
}
fn main() -> i32 {
let p = Point { x: 10, y: 20 };
p.x + p.y // 30
}
Struct fields are accessed using dot notation: value.field_name.
All fields MUST be initialized when creating a struct instance.
Field names MUST be unique within a struct.
Memory Layout
The in-memory layout of struct types is implementation-defined (see 1.3:6): the implementation chooses and documents it. The current implementation prioritizes simplicity over space efficiency, and the layout may change between versions. A portable program must not depend on specific field offsets, padding, or overall size beyond what the specification guarantees; a program that inspects layout (for example through unchecked raw-pointer access) observes an implementation-defined result.
Non-zero-sized types in Rue occupy one or more 8-byte slots. Scalar types (i8, i16, i32, i64, u8, u16, u32, u64, bool) each occupy one slot.
Struct fields are laid out in memory in declaration order, with each field occupying a number of slots determined by its type.
The size of a struct is the sum of the sizes of all its fields. There is no padding between fields or at the end of the struct.
// A struct with two i32 fields occupies 2 slots (16 bytes)
struct Point { x: i32, y: i32 }
// A struct with a nested struct occupies the sum of all nested field slots
struct Line { start: Point, end: Point } // 4 slots (32 bytes)
Structs with one or more fields have 8-byte alignment. Empty structs (zero-sized types) have 1-byte alignment.
A struct with no fields is a zero-sized type. See Zero-Sized Types for the general definition.
// An empty struct is a zero-sized type
struct Empty {}
fn main() -> i32 {
let e = Empty {};
@size_of(Empty) // 0
}
Struct Literals
In a struct literal, field initializers may appear in any order. Each initializer is matched to a declared field by name; the order in which fields are written need not match the declaration order. Providing the same field more than once, omitting a field, or naming a field the struct does not declare are all errors (see 3.6:5, 3.6:6).
Regardless of the order in which fields are written, the resulting value stores each field in the slot determined by declaration order (3.6:9). Field initializer expressions are still evaluated in source order (left-to-right as written; see 4.0:9), so a field written earlier is evaluated earlier even when it occupies a later slot.
struct Point { x: i32, y: i32 }
fn main() -> i32 {
// Fields given out of declaration order; matched by name.
let p = Point { y: 20, x: 10 };
p.x - p.y // -10, i.e. 10 - 20
}