Struct

From Rust Community Wiki
Jump to navigation Jump to search

Structs are types that wrap values of other types, which are called its fields.

Structs that contain no values or only zero-sized types are also zero-sized; they only exist in the type system, but are completely optimized away at runtime. Structs containing exactly one value are called newtypes. They are used to give an existing type a new meaning and/or new functionality.

Structs are equivalent to struct types in C and the Lisp family, and to record types of the ML family; they are sometimes called product types.

Struct fields are private by default and can have visibility modifiers.

Example[edit | edit source]

struct Foo {
    a: i32,
    b: bool,
}

// create an instance of Foo
let mut foo = Foo {
    a: 42,
    b: true,
};

// update a single field
foo.a += 1;

// destructure Foo into its fields
let Foo { a, b } = foo;

Types of structs[edit | edit source]

Syntactically, there are three types of structs: Regular structs, tuple structs and unit structs.

Regular structs[edit | edit source]

Regular structs have curly braces ({}) and named fields as in the example above. When instantiating a regular struct or pattern-matching on it, the fields can be specified in any order, so reordering the fields of a regular struct is backwards compatible, but renaming them is not.

Tuple structs[edit | edit source]

Tuple structs use parentheses (()) instead of curly braces; the fields are unnamed, and are accessed with their index, starting from zero:

struct Foo(i32, bool);

let mut foo = Foo(42, true);
foo.0 += 1;

let Foo(a, b) = foo;
// this is equivalent to
// let Foo { 0: a, 1: b } = foo;

Unlike regular structs, the field order in tuple structs is significant.

Tuple structs are callable, so they can be used in higher-order functions like a closure, function or function pointer. Furthermore, while all types of structs can be instantiated and pattern-matched with curly braces ({}), only tuple structs can be instantiated and pattern-matched with parentheses.

Unit structs[edit | edit source]

When declaring a struct with no fields, it is possible to omit the braces. This means that the braces also have to be omitted when instantiating the struct or pattern-matching on it:

struct Foo;

let foo = Foo;
let Foo = foo;

Struct update syntax[edit | edit source]

The struct update syntax is a syntactic sugar to make initializing a struct easier in certain cases. It copies or moves the missing fields from a previous value of the same struct:

struct Foo(i32, String, bool);

fn update(prev: Foo) -> Foo {
    Foo { 0: 42, ..prev }
    // this is equivalent to
    // Foo { 0: 42, 1: prev.1, 2: prev.2 }
}

The struct update syntax (..prev) must come after all specified fields, and can't be followed by a comma (,). The omitted fields must be visible in the current module.

Fill in default fields[edit | edit source]

The struct update syntax can be used with the DefaultThis links to official Rust documentation trait to fill in default fields:

#[derive(Default)]
struct Foo {
    a: i32,
    b: String,
    c: bool,
}

let foo = Foo {
    a: 42,
    ..Default::default()
};

Accessing fields[edit | edit source]

Fields are accessed with the dot (.) syntax. Like method calls, this performs auto-dereferencing, so it's possible to access fields of a struct through smart pointers such as BoxThis links to official Rust documentation. Accessing a field is a place expression.

If the type of a field doesn't implement CopyThis links to official Rust documentation and the struct doesn't implement DropThis links to official Rust documentation, the field can be moved out of the struct. Rust ensures that each field is moved out of a struct at most once, and that the struct isn't passed to a function after that.

Example[edit | edit source]

struct Foo {
    a: i32,
    b: String,
    c: bool,
}

fn f(mut owned: Foo, borrowed: &mut Foo) {
    // move field out of struct:
    let b = owned.b;
    // this is equivalent to
    // let Foo { b, .. } = owned;

    // borrow field:
    let a: &i32 = &borrowed.a;
    let c: &bool = &owned.c;

    // assign to field:
    owned.a = 42;
    borrowed.a = 42;
}

Memory representation[edit | edit source]

The default representation of structs is unspecified. Rust will make sure that all fields are properly aligned by adding padding bytes and possibly reordering the fields. This behavior can be customized with the #[repr] attribute. To prevent field reordering and guarantee the same representation as an equivalent struct in C, #[repr(C)] must be used. If the struct contains only one value that isn't zero-sized, #[repr(transparent)] can be used to ensure that the struct has the same representation as its inner type.