Custom Types with Struct
Move's type system shines when it comes to defining custom types. User defined types can be custom tailored to the specific needs of the application. Not just on the data level, but also in its behavior. In this section we introduce the struct definition and how to use it.
Struct
To define a custom type, you can use the struct
keyword followed by the name of the type. After
the name, you can define the fields of the struct. Each field is defined with the
field_name: field_type
syntax. Field definitions must be separated by commas. The fields can be of
any type, including other structs.
Move does not support recursive structs, meaning a struct cannot contain itself as a field.
/// A struct representing an artist.
public struct Artist {
/// The name of the artist.
name: String,
}
/// A struct representing a music record.
public struct Record {
/// The title of the record.
title: String,
/// The artist of the record. Uses the `Artist` type.
artist: Artist,
/// The year the record was released.
year: u16,
/// Whether the record is a debut album.
is_debut: bool,
/// The edition of the record.
edition: Option<u16>,
}
In the example above, we define a Record
struct with five fields. The title
field is of type
String
, the artist
field is of type Artist
, the year
field is of type u16
, the is_debut
field is of type bool
, and the edition
field is of type Option<u16>
. The edition
field is of
type Option<u16>
to represent that the edition is optional.
Structs are private by default, meaning they cannot be imported and used outside of the module they are defined in. Their fields are also private and can't be accessed from outside the module. See visibility for more information on different visibility modifiers.
Fields of a struct are private and can only be accessed by the module defining the struct. Reading and writing the fields of a struct in other modules is only possible if the module defining the struct provides public functions to access the fields.
Create and use an instance
We described how struct definition works. Now let's see how to initialize a struct and use it. A
struct can be initialized using the struct_name { field1: value1, field2: value2, ... }
syntax.
The fields can be initialized in any order, and all of the fields must be set.
let mut artist = Artist {
name: b"The Beatles".to_string()
};
In the example above, we create an instance of the Artist
struct and set the name
field to a
string "The Beatles".
To access the fields of a struct, you can use the .
operator followed by the field name.
// Access the `name` field of the `Artist` struct.
let artist_name = artist.name;
// Access a field of the `Artist` struct.
assert!(artist.name == b"The Beatles".to_string());
// Mutate the `name` field of the `Artist` struct.
artist.name = b"Led Zeppelin".to_string();
// Check that the `name` field has been mutated.
assert!(artist.name == b"Led Zeppelin".to_string());
Only module defining the struct can access its fields (both mutably and immutably). So the above
code should be in the same module as the Artist
struct.
Unpacking a struct
Structs are non-discardable by default, meaning that the initiated struct value must be used: either
stored or unpacked. Unpacking a struct means deconstructing it into its fields. This is done using
the let
keyword followed by the struct name and the field names.
// Unpack the `Artist` struct and create a new variable `name`
// with the value of the `name` field.
let Artist { name } = artist;
In the example above we unpack the Artist
struct and create a new variable name
with the value
of the name
field. Because the variable is not used, the compiler will raise a warning. To
suppress the warning, you can use the underscore _
to indicate that the variable is intentionally
unused.
// Unpack the `Artist` struct and ignore the `name` field.
let Artist { name: _ } = artist;
Further reading
- Structs in the Move Reference.