Defining a struct Type

The definition of a struct type starts with the keyword struct, followed by the struct name and the struct definition body defined in braces. The struct definition body can define a series of member variables, member properties (see Properties), static initializers, constructors, and member functions.

struct Rectangle {
    let width: Int64
    let height: Int64

    public init(width: Int64, height: Int64) {
        this.width = width
        this.height = height
    }

    public func area() {
        width * height
    }
}

The preceding example defines a struct type named Rectangle. It has two member variables width and height of the Int64 type, a constructor with two parameters of the Int64 type (with the constructor defined by the keyword init, and the member variables initialized in the function body), as well as a member function area (returning the product of width and height).

Note:

struct can be defined only on top of a source file.

struct Member Variables

struct member variables can be grouped into instance member variables and static member variables (modified by static modifiers and must have initial values). The difference lies in that instance member variables can be accessed only through struct instances (If a is an instance of the T type, a is a value of the T type), whereas static member variables can be accessed only through the struct type names.

When you define instance member variables, you are not required to set initial values but must specify the types, such as width and height in the preceding example. However, you can also set initial values as follows:

struct Rectangle {
    let width = 10
    let height = 20
}

struct Static Initializers

struct allows you to define a static initializer and initialize static member variables with assignment expressions in the static initializer.

A static initializer starts with static init, followed by a parameter list without parameters and a function body. It cannot be modified by an access modifier. All static member variables must be initialized in the function body. Otherwise, an error is reported during compilation.

struct Rectangle {
    static let degree: Int64
    static init() {
        degree = 180
    }
}

A struct defines a maximum of one static initializer. In other cases, a redefinition error is reported.

struct Rectangle {
    static let degree: Int64
    static init() {
        degree = 180
    }
    static init() { // Error, redefinition with the previous static init function
        degree = 180
    }
}

struct Constructors

struct supports two types of constructors: common constructors and primary constructors.

A common constructor starts with the keyword init, followed by the parameter list and function body. All instance member variables must be initialized in the function body. Otherwise, an error is reported during compilation. If a member variable name cannot be distinguished from a parameter name, you can add this before the member variable name to indicate the current instance of the struct.

struct Rectangle {
    let width: Int64
    let height: Int64

    public init(width: Int64, height: Int64) { // Error, 'height' is not initialized in the constructor
        this.width = width
    }
}

A struct can define multiple common constructors, but they must be overloaded (see Function Overloading). Otherwise, a redefinition error is reported.

struct Rectangle {
    let width: Int64
    let height: Int64

    public init(width: Int64) {
        this.width = width
        this.height = width
    }

    public init(width: Int64, height: Int64) { // Ok: overloading with the first init function
        this.width = width
        this.height = height
    }

    public init(height: Int64) { // Error, redefinition with the first init function
        this.width = height
        this.height = height
    }
}

In addition to defining several common constructors named init, a struct can also define a maximum of one primary constructor. The primary constructor has the same name as the struct type. The parameter list of the primary constructor can contain two types of parameters: common parameters and member variable parameters (with let or var added before the parameter names). Member variable parameters define member variables and constructor parameters.

The definition of a struct can be simplified by using a primary constructor. For example, the Rectangle struct that contains an init constructor can be simplified as follows:

struct Rectangle {
    public Rectangle(let width: Int64, let height: Int64) {}
}

Common parameters can also be defined in the parameter list of a primary constructor. For example:

struct Rectangle {
    public Rectangle(name: String, let width: Int64, let height: Int64) {}
}

If the definition of a struct does not contain a custom constructor (including the primary constructor) and all instance member variables have initial values, a parameterless constructor is automatically generated (Calling the parameterless constructor creates an object whose instance member variable values equal to their initial values). Otherwise, the parameterless constructor is not automatically generated. The following struct definition displays an automatically generated parameterless constructor:

struct Rectangle {
    let width: Int64 = 10
    let height: Int64 = 10
    /* Auto-generated memberwise constructor:
    public init() {
    }
    */
}

struct Member Functions

struct member functions are classified into instance member functions and static member functions (modified by the static modifier). The difference lies in that the former can be accessed only through struct instances, whereas the latter can be accessed only through struct type names. Static member functions cannot access instance member variables or call instance member functions. In contrast, instance member functions can access static member variables and static member functions.

In the following example, area and typeName are an instance member function and a static member function, respectively.

struct Rectangle {
    let width: Int64 = 10
    let height: Int64 = 20

    public func area() {
        this.width * this.height
    }

    public static func typeName(): String {
        "Rectangle"
    }
}

An instance member function can access its member variables through this. For example:

struct Rectangle {
    let width: Int64 = 1
    let height: Int64 = 1

    public func area() {
        this.width * this.height
    }
}

Access Modifiers of a struct Member

The members of a struct (including member variables, member properties, constructors, member functions, and operator functions (see Operator Overloading)) are modified by four access modifiers: private, internal (default), protected, and public.

  • private: A member is visible only in a defined struct.
  • internal: A member is visible only in a package and its subpackages (including all nested subpackages). For details, see Package.
  • protected: A member is visible only in a module. For details, see Package.
  • public: A member is visible both inside and outside the module.

In the following example, width is modified by public and can be accessed outside the class. height is modified by the default access modifier and is visible only in the package and its subpackages.

package a
public struct Rectangle {
    public var width: Int64
    var height: Int64
    private var area: Int64
    ...
}

func samePkgFunc() {
    var r = Rectangle(10, 20)
    r.width = 8               // Ok: public 'width' can be accessed here
    r.height = 24             // Ok: 'height' has no modifier and can be accessed here
    r.area = 30               // Error, private 'area' can't be accessed here
}
package b
import a.*
main() {
    var r = Rectangle(10, 20)
    r.width = 8               // Ok: public 'width' can be accessed here
    r.height = 24             // Error, no modifier 'height' can't be accessed here
    r.area = 30               // Error, private 'area' can't be accessed here
}

Disabling Recursive structs

Both recursive and mutually recursive structs are invalid. For example:

struct R1 { // Error, 'R1' recursively references itself
    let other: R1
}
struct R2 { // Error, 'R2' and 'R3' are mutually recursive
    let other: R3
}
struct R3 { // Error, 'R2' and 'R3' are mutually recursive
    let other: R2
}