Structured data
When working with data, you often find yourself needing to group information together. This is where a struct
could come into play. A struct, or structure, is a custom data type that lets you name and package together multiple related values that make up a meaningful group. If you’re familiar with an object-oriented language, a struct is like an object’s data attributes.
Defining structs
To define a struct, we enter the keyword struct
and name the entire struct. A struct’s name should describe the significance of the pieces of data being grouped together. Then, inside curly brackets, we define the names and types of the pieces of data, which we call fields. The following example shows a struct that stores information about a user account.
struct User {
username: string
email: string
sign_in_count: int
active: bool
}
Structs can be nested as a type inside other structs. For example, we could assign each user an address, which itself is a struct.
struct Address {
street: string
number: int
postal_code: string
city: string
}
struct User {
username: string
email: string
address: Address
}
Instantiating structs
To use a struct after we’ve defined it, we create an instance of that struct by specifying concrete values for each of the fields. We create an instance by stating the name of the struct and then add curly brackets containing key: value
pairs, where the keys are the names of the fields and the values are the data we want to store in those fields. We don’t have to specify the fields in the same order in which we declared them in the struct. In other words, the struct definition is like a general template for the type, and instances fill in that template with particular data to create values of the type. Let's use our User
struct from a previous example and create an user called alice
.
struct User {
username: string
email: string
sign_in_count: int
active: bool
}
let alice = new User {
email: "alice@example.com"
username: "alice"
sign_in_count: 1
active: true
}
To get a specific value from a struct, we can use dot notation. If we wanted just alice's email address, we could use alice.email
wherever we wanted to use this value. Fields of structs can also be reassigned using the dot notation:
let alice = new User {
email: "alice@example.com"
username: "alice"
sign_in_count: 1
active: true
}
alice.sign_in_count = 2
Struct methods
Antimony supports the concept of methods. A method can be described as a function on a struct. Let's take a look at a struct implementing a method.
struct User {
first_name: string
last_name: string
fn full_name(): string {
return self.first_name + " " + self.last_name
}
}
Every instance of the User
struct can now call full_name()
. Note the usage of the word self
. self
is a special keyword referencing the struct instance the method was called on. Say we had the following instance of User
, and called the full_name()
method on it.
let alice = new User {
first_name: "Jon"
last_name: "Doe"
}
println(alice.full_name())
The full_name
method described above will return the first name, a space and the last name of the user. If we run this code, we should see the expected output:
$ sb run main.sb
Jon Doe
Methods behave just like functions. They can return a value and take parameters. The only difference is the self
keyword, which allows you to execute it on a specific instance of a struct.