Structs and Resources

A struct is a user-defined data structure containing typed fields. Structs can store any non-reference type, including other structs.

We often refer to struct values as resources if they cannot be copied and cannot be dropped. In this case, resource values must have ownership transferred by the end of the function. This property makes resources particularly well served for defining global storage schemas or for representing important values (such as a token).

By default, structs are linear and ephemeral. By this we mean that they: cannot be copied, cannot be dropped, and cannot be stored in global storage. This means that all values have to have ownership transferred (linear) and the values must be dealt with by the end of the program's execution (ephemeral). We can relax this behavior by giving the struct abilities which allow values to be copied or dropped and also to be stored in global storage or to define global storage schemas.

Defining Structs

Structs must be defined inside a module:

module 0x2::m {
    struct Foo { x: u64, y: bool }
    struct Bar {}
    struct Baz { foo: Foo, }
    //                   ^ note: it is fine to have a trailing comma
}

Structs cannot be recursive, so the following definition is invalid:

module 0x2::m {
  struct Foo { x: Foo }
  //              ^ error! Foo cannot contain Foo
}

As mentioned above: by default, a struct declaration is linear and ephemeral. So to allow the value to be used with certain operations (that copy it, drop it, store it in global storage, or use it as a storage schema), structs can be granted abilities by annotating them withhas <ability>:

module 0x2::m {
  struct Foo has copy, drop { x: u64, y: bool }
}

For more details, see the annotating structs section.

Naming

Structs must start with a capital letter A to Z. After the first letter, struct names can contain underscores _, letters a to z, letters A to Z, or digits 0 to 9.

This naming restriction of starting with A to Z is in place to give room for future language features. It may or may not be removed later.

Using Structs

Creating Structs

Values of a struct type can be created (or "packed") by indicating the struct name, followed by value for each field:

If you initialize a struct field with a local variable whose name is the same as the field, you can use the following shorthand:

This is called sometimes called "field name punning".

Destroying Structs via Pattern Matching

Struct values can be destroyed by binding or assigning them patterns.

Borrowing Structs and Fields

The & and &mut operator can be used to create references to structs or fields. These examples include some optional type annotations (e.g., : &Foo) to demonstrate the type of operations.

It is possible to borrow inner fields of nested structs:

You can also borrow a field via a reference to a struct:

Reading and Writing Fields

If you need to read and copy a field's value, you can then dereference the borrowed field:

If the field is implicitly copyable, the dot operator can be used to read fields of a struct without any borrowing. (Only scalar values with the copy ability are implicitly copyable.)

Dot operators can be chained to access nested fields:

However, this is not permitted for fields that contain non-primitive types, such a vector or another struct:

The reason behind this design decision is that copying a vector or another struct might be an expensive operation. It is important for a programmer to be aware of this copy and make others aware with the explicit syntax *&.

In addition, reading from fields, the dot syntax can be used to modify fields, regardless of the field being a primitive type or some other struct.

The dot syntax also works via a reference to a struct:

Privileged Struct Operations

Most struct operations on a struct type T can only be performed inside the module that declaresT:

  • Struct types can only be created ("packed"), destroyed ("unpacked") inside the module that defines the struct.

  • The fields of a struct are only accessible inside the module that defines the struct.

Following these rules, if you want to modify your struct outside the module, you will need to provide public APIs for them. The end of the chapter contains some examples of this.

However, struct types are always visible to another module or script:

Note that structs do not have visibility modifiers (e.g., public or private).

Ownership

As mentioned above in Defining Structs, structs are by default linear and ephemeral. This means they cannot be copied or dropped. This property can be very useful when modeling real world resources like money, as you do not want money to be duplicated or get lost in circulation.

To fix the second example (fun destroying_resource1), you would need to manually "unpack" the resource:

Recall that you are only able to deconstruct a resource within the module in which it is defined. This can be leveraged to enforce certain invariants in a system, for example, conservation of money.

If on the other hand, your struct does not represent something valuable, you can add the abilitiescopy and drop to get a struct value that might feel more familiar from other programming languages:

Storing Resources in Global Storage

Only structs with the key ability can be saved directly inpersistent global storage. All values stored within those key structs must have the store ability. See the ability andglobal storage chapters for more detail.

Examples

Here are two short examples of how you might use structs to represent valuable data (in the case ofCoin) or more classical data (in the case of Point and Circle).

Example 1: Coin

Example 2: Geometry

Last updated