Working Code
Imagine storing user information: name, age, and email. Until now you'd need three separate variables. With a struct, you can bundle them into one:
#[derive(Debug)]
struct User {
name: String,
age: u32,
email: String,
}
fn main() {
let user = User {
name: String::from("Alice"),
age: 25,
email: String::from("alice@example.com"),
};
println!("{:?}", user);
println!("Name: {}", user.name);
}
Adding #[derive(Debug)] lets you print the entire struct with {:?}. Don't worry about the details yet — for now, think of it as "magic that enables debug output."
Try It Yourself
- Add an
is_active: boolfield toUserand set it totrue. - Try modifying
user.namewithuser.name = String::from("Bob");. What error do you get?
"Why?" — Structs and Ownership
Structs are an extension of ownership. When a User owns a String field, the struct is the owner of that string. When the struct is dropped, its field values are cleaned up too.
Modifying Fields
To change a struct's values, the variable itself must be mut. Rust doesn't allow individual fields to be mutable — the whole struct is mutable or the whole struct is immutable.
#[derive(Debug)]
struct User {
name: String,
age: u32,
}
fn main() {
let mut user = User {
name: String::from("Alice"),
age: 25,
};
user.age = 26; // OK because it's mut!
println!("{:?}", user);
}
Adding Methods — impl Blocks
To give a struct behavior, use an impl block:
#[derive(Debug)]
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
// Method: borrows self immutably
fn area(&self) -> f64 {
self.width * self.height
}
fn is_square(&self) -> bool {
self.width == self.height
}
// Associated function: no self (constructor pattern)
fn square(size: f64) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn main() {
let rect = Rectangle { width: 10.0, height: 5.0 };
println!("Area: {}", rect.area());
println!("Square? {}", rect.is_square());
let sq = Rectangle::square(7.0);
println!("Square area: {}", sq.area());
}
&self, &mut self, self — Borrowing Rules Apply
The first parameter of a method determines how self is used. It follows the same borrowing rules from previous lessons!
| Parameter | Meaning | Analogy |
| ----------- | ---------------------------- | ------------------------------------ |
| &self | Read-only (immutable borrow) | Reading a library book |
| &mut self | Can modify (mutable borrow) | Editing with write permission |
| self | Takes ownership | Taking the book and not returning it |
#[derive(Debug)]
struct Counter {
count: i32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
fn value(&self) -> i32 {
self.count // read-only
}
fn increment(&mut self) {
self.count += 1; // modify
}
}
fn main() {
let mut c = Counter::new();
c.increment();
c.increment();
println!("Count: {}", c.value());
}
increment modifies the value, so it uses &mut self. value only reads, so it uses &self.
Learn More: Putting &str in a Struct
If you try to put &str in a struct field, you get this error:
struct User {
name: &str, // error!
}
error[E0106]: missing lifetime specifier
To store a borrowed value (&str) in a struct, you must specify a lifetime — telling the compiler "how long this borrowed value stays valid."
Lifetimes are covered in Lesson 15. For now, remember that using String for struct fields is the safe default!
Exercise 1. Create a Book struct and add a summary method.
// TODO: Define Book struct (title: String, author: String, pages: u32)
// TODO: Add #[derive(Debug)]
// TODO: Add a summary method in an impl block
// Return a String like "《Title》 - Author (N pages)"
fn main() {
let book = Book {
title: String::from("Rust Programming"),
author: String::from("Dale"),
pages: 300,
};
println!("{}", book.summary());
// Output: 《Rust Programming》 - Dale (300 pages)
}
Hint: The format! macro comes in handy.
Exercise 2. Add a reset method to the Counter struct. It should set count back to 0.
impl Counter {
// ... existing methods ...
fn reset(&mut self) {
// Complete this
}
}
Hint: Since you're modifying a value, what kind of self do you need?
Q1. How do you make struct fields modifiable?
- A) Put
mutbefore the field name - B) Declare the variable with
let mut - C) Add
#[derive(Mutable)] - D) Put
mutbefore the field type
Q2. What does &self mean in an impl block?
- A) It takes ownership of the struct
- B) It borrows the struct immutably (read-only)
- C) It borrows the struct mutably (can modify)
- D) It creates a new struct
Q3. What do you call a function like Rectangle::square(5.0) that's called without self?
- A) A method
- B) An associated function
- C) A closure
- D) A macro