Structs are essentially tuples with named fields. For example:
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// Normal Declaration
let user1 = User {
email: String::from("contact@daltoncole.com"),
username: String::from("drc"),
active: true,
sign_in_count: 1,
};
let user2 = build_user_annoying_way(String::from("bob@bob.bob"), String::from("bob"));
let user3 = build_user_easy_way(String::from("john@bob.com"), String::from("john"));
// Struct Update (quick way to change only some fields from another instance)
let user4 = User {
email: String::from("me@daltoncole.com"),
username: String::from("crd"),
..user1 // Copies the rest of the data from user 1
};
println!(
"The users are: {}, {}, {}, and {}",
user1.username, user2.username, user3.username, user4.username
)
}
fn build_user_annoying_way(email: String, username: String) -> User {
User {
email: email,
username: username,
active: true,
sign_in_count: 1,
}
}
// Since the variable names share the same name as the field,
// we can just use the variables instead
fn build_user_easy_way(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
Struct methods are implemented in impl
blocks. Multiple methods can be in a single impl
block and you can have multiple impl
blocks per struct.
The general syntax of methods are very similar to python methods, where instance methods require &self
(just using self
is allowed but rare). To call "class" level methods, the namespace operator ::
is required.
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// Self's type is automatically the struct's type
fn area(&self) -> u32 {
self.width * self.height
}
fn fits_inside(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
impl Rectangle {
// Example where you wouldn't use self
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn main() {
let rect1 = Rectangle {
width: 25,
height: 40,
};
let rect2 = Rectangle {
width: 100,
height: 45,
};
println!("Area of rect1: {}", rect1.area());
println!("Fits inside: {}", rect1.fits_inside(&rect2));
// Create a square rectangle. Use the namespace syntax here
let square = Rectangle::square(7);
println!("Area of square: {}", square.area());
}
You can also create structs that behave like tuples, i.e. structs without named fields. This can be useful when you want to pass tuples that contain specific information into a function.
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let white = Color(255, 255, 255);
let origin = Point(0, 0, 0);
print_color(white);
//print_color(origin); // Breaks
}
fn print_color(color: Color) {
// Use "." followed by the index to index into a Tuple Struct
println!("({}, {}, {})", color.0, color.1, color.2);
}