#9 Structs
struct, impl, methods
Defining structs
A struct (short for structure) lets you group related data together under a single name. Each piece of data is called a field:
struct User {
username: String,
email: String,
active: bool,
sign_in_count: u64,
}Structs are similar to tuples in that they hold multiple values, but each field has a name, making the data self-documenting.
Creating instances
To create an instance of a struct, specify values for every field:
let user1 = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
active: true,
sign_in_count: 1,
};
// Access fields with dot notation
println!("Username: {}", user1.username);
// Struct update syntax — create a new struct from an existing one
let user2 = User {
email: String::from("bob@example.com"),
..user1 // Fill remaining fields from user1
};With field init shorthand, if a variable has the same name as a field, you can write just the name:
fn build_user(email: String, username: String) -> User {
User {
username, // Same as username: username
email, // Same as email: email
active: true,
sign_in_count: 1,
}
}Methods with impl
Methods are functions defined within an impl block. Their first parameter is always &self (or &mut self), which refers to the struct instance:
#[derive(Debug)]
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect = Rectangle { width: 30.0, height: 50.0 };
println!("Rectangle: {rect:?}");
println!("Area: {}", rect.area());
let small = Rectangle { width: 10.0, height: 20.0 };
println!("Can hold: {}", rect.can_hold(&small));
}The #[derive(Debug)] attribute automatically implements the Debug trait, letting you print the struct with {:?} or {:#?} for pretty printing.
Associated functions
Functions in an impl block that do not take self are called associated functions. They are often used as constructors:
impl Rectangle {
fn square(size: f64) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
// Call with :: syntax
let sq = Rectangle::square(10.0);
println!("Square: {sq:?}");You call associated functions using the :: syntax, similar to String::from() or Vec::new().
Tuple structs
Tuple structs have a name but their fields are not named. They are useful for creating distinct types:
struct Color(u8, u8, u8);
struct Point(i32, i32, i32);
let orange = Color(255, 128, 0);
let origin = Point(0, 0, 0);
// Access by index
println!("Red: {}", orange.0);
println!("Y: {}", origin.1);
// Color and Point are different types,
// even though both hold three valuesEven if two tuple structs have the same field types, they are different types. A function that takes a Color will not accept a Point.
Your turn
Try running the structs demo with cargo run and cargo check in the terminal below: