Structs are similar to tuples, but it has a name so we don’t need to use the ordering to obtain the values.
To define a struct, we use the keyword struct
and name the entire struct.
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
fn main() {
let mut user1 = User {
active: true,
username: String::from("someusername123"),
email: String::from("[email protected]"),
sign_in_count: 1,
};
// only mutatable if the struct itself is of the mut type
user1.email = String::from("[email protected]");
}
Notice that if a parameter’s name is same as the field name, we can just pass it in instead of specifying the parameter
fn build_user(email: String, username: String) -> User {
User {
active: true,
username,
email,
sign_in_count: 1,
}
}
Rust also support spread using ...
let user2 = User {
email: String::from("[email protected]"),
..user1
};
by doing so, we are only updating the email and remain everything else the same in user2. This uses =
like assignment. Thus, after we did the above operation, user1 might no longer be usable as a whole(some of the values will be moved to user2 if they didn’t implement the Copy
trait).
Rust supports tuple structs which can be thought of named tuples but don’t have names associated with their fields.
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}
black
and origin
values are different types because they’re instances of different structs. A function that takes a parameter of type Color
cannot take a Points
as an argument.
Those are structs that don’t have any fields, which behaved like ()
. This can be useful when we need to implement a trait on some type but don’t have any data to store. More on this later.
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
Methods are similar to functions: we declare them with the fn
keyword and a name
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
To define a function within the context of a struct, we start an impl
block for that struct. Everything within this impl
block will be associated with the struct. Then we can put function within the impl
brackets and change the first parameter to be &self
in the signature and everywhere within the body. If this method is gonna mutate the data, we need to change the parameter to &mut self