Records vs Tuples

Mun supports two types of structures: record structs and tuple structs. A record struct definition specifies both the name and type of each piece of data, allowing you to retrieve the field by name. For example, Listing 4-1 shows a record struct that stores a 2-dimensional vector.

pub struct Vector2 {
    x: f32,
    y: f32,
}

Listing 4-1: A record struct definition for a 2D vector

In contrast, tuple struct definitions omit field names; only specifying the field types. Using a tuple struct makes sense when you want to associate a name with a tuple or distinguish it from other tuples' types, but naming each field would be redundant. Listing 4-2 depicts a tuple struct that stores a 3-dimensional vector.

pub struct Vector3(f32, f32, f32)

Listing 4-2: A tuple struct definition for a 3D vector

Create a Struct Instance

To use a record struct, we create an instance of that struct by stating the name of the struct and then add curly braces containing key: value pairs for each of its fields. The keys have to correspond to the field names in the struct definition, but can be provided in any order. Let's create an instance of our Vector2, as illustrated in Listing 4-3.

pub struct Vector2 {
    x: f32,
    y: f32,
}
let xy = Vector2 {
    x: 1.0,
    y: -1.0,
};

Listing 4-3: Creating a Vector2 instance

To create an instance of a tuple struct, you only need to state the name of the struct and specify a comma-separated list of values between round brackets - as shown in Listing 4-4. As values are not linked to field names, they have to appear in the order specified by the struct definition.

pub struct Vector3(f32, f32, f32)
let xyz = Vector3(-1.0, 0.0, 1.0);

Listing 4-4: Creating a Vector3 instance

Field Init Shorthand

It often makes sense to name function variables the same as the fields of a record struct. Instead of having to repeat the x and y field names, the field init shorthand syntax demonstrated in Listing 4-5 allows you to avoid repetition.

pub struct Vector2 {
    x: f32,
    y: f32,
}
pub fn vector2_new(x: f32, y: f32) -> Vector2 {
    Vector2 { x, y }
}

Listing 4-5: Creating a Vector2 instance using the field init shorthand syntax

Access Struct Fields

To access a record's fields, we use the dot notation: vector.x. The dot notation can be used both to retrieve and to assign a value to the record's field, as shown in Listing 4-6. As you can see, the record's name is used to indicate that the function expects two Vector2 instances as function arguments and returns a Vector2 instance as result.

pub struct Vector2 {
    x: f32,
    y: f32,
}
pub fn vector2_add(lhs: Vector2, rhs: Vector2) -> Vector2 {
    lhs.x += rhs.x;
    lhs.y += rhs.y;
    lhs
}

Listing 4-6: Using Vector2 instances' fields to calculate their addition

A tuple struct doesn't have field names, but instead accesses fields using indices - starting from zero - corresponding to a field's position within the struct definition (see Listing 4-7).

pub struct Vector3(f32, f32, f32)
pub fn vector3_add(lhs: Vector3, rhs: Vector3) -> Vector3 {
    lhs.0 += rhs.0;
    lhs.1 += rhs.1;
    lhs.2 += rhs.2;
    lhs
}

Listing 4-7: Using Vector3 instances' fields to calculate their addition

Unit Struct

Sometimes it can be useful to define a struct without any fields. These so-called unit structs are defined using the struct keyword and a name, as shown in Listing 4-8.

pub struct Unit;

Listing 4-8: A unit struct definition.