Rust Structs
A struct groups related values into one named type. Where a tuple is a quick, unnamed bundle, a struct gives every field a name — which makes the code that uses it far easier to read.
Defining and creating a struct
Section titled “Defining and creating a struct”struct Person { name: String, age: u32,}
fn main() { let alice = Person { name: String::from("Alice"), age: 30, };
println!("{} is {}", alice.name, alice.age);}Output:
Alice is 30Compare this to a tuple ("Alice", 30) — with a struct, alice.name and alice.age are self-explanatory. You don’t have to remember that field 0 is the name and field 1 is the age.
Making a struct mutable
Section titled “Making a struct mutable”Like variables, struct instances are immutable by default. Add mut to change a field after creating it:
struct Person { name: String, age: u32,}
fn main() { let mut alice = Person { name: String::from("Alice"), age: 30, };
alice.age += 1; println!("{} is now {}", alice.name, alice.age);}Adding behavior with methods
Section titled “Adding behavior with methods”You can attach functions to a struct using an impl block. Inside, &self refers to the specific instance the method was called on:
struct Person { name: String, age: u32,}
impl Person { fn greet(&self) { println!("Hi, I'm {} and I'm {}", self.name, self.age); }}
fn main() { let alice = Person { name: String::from("Alice"), age: 30 }; alice.greet();}Output:
Hi, I'm Alice and I'm 30Constructor-style functions
Section titled “Constructor-style functions”A common pattern is a new function inside impl that builds an instance for you, so callers don’t have to write out every field by hand:
struct Person { name: String, age: u32,}
impl Person { fn new(name: &str, age: u32) -> Person { Person { name: name.to_string(), age } }}
fn main() { let bob = Person::new("Bob", 25); println!("{} is {}", bob.name, bob.age);}Notice age on its own inside Person { name: name.to_string(), age } — when a variable has the exact same name as the field, you can skip writing age: age and just write age. Rust calls this field init shorthand.
Printing a struct with derive
Section titled “Printing a struct with derive”Try println!("{:?}", alice) on a struct as written above, and it won’t compile — Rust doesn’t know how to debug-print a custom type unless you tell it to. Add #[derive(Debug)] above the struct to get that for free:
#[derive(Debug)]struct Person { name: String, age: u32,}
fn main() { let alice = Person { name: String::from("Alice"), age: 30 }; println!("{:?}", alice);}Output:
Person { name: "Alice", age: 30 }#[derive(...)] is Rust’s way of auto-generating common functionality for a type — you’ll see Debug, and later Clone and others, added this way constantly.
Practice
Section titled “Practice”1. Rectangle methods
Section titled “1. Rectangle methods”Make a Rectangle struct with width and height fields (u32). Give it two methods: area, which returns the area, and is_square, which returns true when both sides are equal. Hint: both methods only read the fields, so they take &self.
Show solution
struct Rectangle { width: u32, height: u32,}
impl Rectangle { fn area(&self) -> u32 { self.width * self.height }
fn is_square(&self) -> bool { self.width == self.height }}
fn main() { let wide = Rectangle { width: 8, height: 3 }; let square = Rectangle { width: 5, height: 5 };
println!("wide: area {}, square? {}", wide.area(), wide.is_square()); println!("square: area {}, square? {}", square.area(), square.is_square());}Output:
wide: area 24, square? falsesquare: area 25, square? true2. A bank account
Section titled “2. A bank account”Make an Account struct with an owner (String) and a balance in cents (u32). Write Account::new("Ana") that starts the balance at 0. Then deposit 50 and 25 cents by changing the field, and print the account in two ways: with a normal sentence and with {:?}. Hint: the variable has to be let mut before you can change account.balance, and {:?} needs #[derive(Debug)].
Show solution
#[derive(Debug)]struct Account { owner: String, balance: u32,}
impl Account { fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }}
fn main() { let mut account = Account::new("Ana"); account.balance += 50; account.balance += 25;
println!("{} has {} cents", account.owner, account.balance); println!("{:?}", account);}Output:
Ana has 75 centsAccount { owner: "Ana", balance: 75 }new sets balance: 0 itself, so callers can’t forget it. That’s a big part of why constructor functions are so common.
3. A list of people
Section titled “3. A list of people”Put three Person values in a vector: Ana (34), Ben (16) and Chen (52). Print the name of everyone who is 18 or older, then print who is the oldest. Hint: loop with for person in &people, and keep track of the oldest person so far with let mut oldest = &people[0];.
Show solution
struct Person { name: String, age: u32,}
fn main() { let people = vec![ Person { name: String::from("Ana"), age: 34 }, Person { name: String::from("Ben"), age: 16 }, Person { name: String::from("Chen"), age: 52 }, ];
let mut oldest = &people[0]; for person in &people { if person.age >= 18 { println!("{} is an adult", person.name); } if person.age > oldest.age { oldest = person; } }
println!("Oldest: {} ({})", oldest.name, oldest.age);}Output:
Ana is an adultChen is an adultOldest: Chen (52)oldest is a reference (&Person) into the vector, not a copy, so nothing gets cloned. Each time we find someone older, we just point oldest at them instead.
- A struct groups named fields into one type:
struct Person { name: String, age: u32 }. - Create an instance with
Person { name: ..., age: ... }; mark itmutto change fields later. - Add behavior with an
implblock;&selfrefers to the instance a method was called on. - Add
#[derive(Debug)]to make a struct printable with{:?}.
Quick check
Section titled “Quick check”Quick check
1. What's the main advantage of a struct over a tuple?
2. What does adding #[derive(Debug)] to a struct let you do?
3. In Person { name: name.to_string(), age }, what does the bare age do?
Score: 0 / 3
Read more
Section titled “Read more”- Defining and Instantiating Structs — The Rust Book: also covers struct update syntax (
..other), tuple structs and unit-like structs. - An Example Program Using Structs — The Rust Book: refactors a rectangle program from loose variables to a tuple to a struct, so you see why structs help.
- Method Syntax — The Rust Book:
&selfvs&mut self, methods with extra parameters, and associated functions likenew. - Structures — Rust by Example: runnable examples of every kind of struct, with a small exercise at the end.
- Derive — Rust by Example: what
#[derive(...)]can generate for you, likeDebug,CloneandPartialEq.