Using Structs to Structure Related Data
Defining and instantiating structs
Struct allows you to compose different type of data together into one big object, just like structs in C.
You will have to name each piece of data that you are using so that you can access them when you instantiate a struct later.
Here is how to define a sample struct (Note you would write this outside of functions):
struct User {
active: bool,
username: String,
email: String,
}
Then to instantiate a struct:
fn main() {
let user1 = User {
active: true,
username: String::from("Ricky"),
email: String::from("irebo@gmail.com"),
};
}
To access the fields that you have instantiated you would use the dot notation, user1.active, user1.username, user1.email
.
To make the struct mutable you would also attach the mut
modifier to the variable. The entire instance of struct must be mutable, Rust doesn't allow partial field mutability.
Field init shorthand
Say you have a function that builds your struct and return it as its return value depending on the parameter you passed:
fn build_user(email: String, username: String) -> User {
User {
active: true,
username: username,
email: email,
}
}
Writing it like this will get repetitive, especially if there are going to be lot of parameter, instead you can use a shorthand, just ignore the key if the parameter that you passed into the function is the same as the key name:
fn build_user(email: String, username: String) -> User {
User {
active: true,
username,
email,
}
}
This is much more concise without the repetition.
Creating instances from other instances with struct update syntax
Sometimes you might want to create a new instances from the old instances, changing some of the old values but keep the rest the same.
You can do it the hard coded way like such:
fn main() {
// created user1 here
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("new email@gmail.com"),
};
}
This works but you have to type out all of the fields that are repeated, a much shorter way to do this is to use struct update syntax:
fn main() {
// create user1 here
let user2 = User {
email: String::from("new email here"),
..user1
}
}
With this syntax, you only have to worry about writing the new value for the new instance, and leave all of the old values to struct update syntax to handle.
..user1
must come last to specify that any remaining fields should be getting their values from the corresponding fields in user1
.
struct update syntax uses =
like an assignment, so it will be moving data. After doing struct update you can no longer use user1
as a whole after creating user2
since data like username
is moved to user2
and not copied!
Tuple struct
You can also create a tuple struct, which is like struct but doesn't have names associated with their fields, they only have type of the fields.
This is useful if you just want to give a simple tuple a name. And separate different type of tuple from each other.
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}
You can access the tuple using the same tuple syntax, tuple.<index number>