Structs
Prerequisites: Vectors
A struct groups related values under one name, with a named field for each value. It's how you create your own data types.
Syntax
struct User {
name: String,
email: String,
age: u32,
active: bool,
}
let user = User {
name: String::from("Ada"),
email: String::from("ada@example.com"),
age: 36,
active: true,
};
println!("{}", user.name);
- Struct names use
PascalCase; field names usesnake_case. - Create an instance by giving a value for every field, in any order. Read fields with dot notation.
- To change a field, the whole instance must be
mut— Rust doesn't allow marking individual fields as mutable. - Field init shorthand — if a variable has the same name as a
field, write
User { name, age, ... }instead ofname: name. - Struct update syntax —
User { age: 40, ..other }copies the remaining fields fromother. - Tuple structs have fields without names:
struct Point(i32, i32);. Access them with.0,.1. They're useful for giving a tuple a distinct type — aMeters(f64)can't be mixed up with aSeconds(f64). #[derive(Debug)]above a struct lets you print it with{:?}or{:#?}.
Example
#[derive(Debug)]
struct User {
name: String,
age: u32,
active: bool,
}
#[derive(Debug)]
struct Point(i32, i32);
fn build_user(name: String, age: u32) -> User {
User {
name, // field init shorthand
age,
active: true,
}
}
fn main() {
let mut user = build_user(String::from("Ada"), 36);
println!("{} is {}, active: {}", user.name, user.age, user.active);
user.age += 1;
println!("After birthday: {}", user.age);
let other = User {
name: String::from("Grace"),
..user // take the remaining fields from `user`
};
println!("{other:?}");
let origin = Point(0, 0);
let p = Point(3, -4);
println!("{origin:?} and ({}, {})", p.0, p.1);
println!("{user:#?}");
}
Expected output
Ada is 36, active: true
After birthday: 37
User { name: "Grace", age: 37, active: true }
Point(0, 0) and (3, -4)
User {
name: "Ada",
age: 37,
active: true,
}
user is still usable after the update syntax because only age
and active were taken from it, and both are Copy types. If
..user had supplied the name field, the String would have moved
out of user.
Common mistakes
- Printing a struct without
#[derive(Debug)]:
error[E0277]: `User` doesn't implement `Debug`
The compiler suggests adding #[derive(Debug)] above the struct.
- Changing a field of an immutable instance.
user.age += 1;requireslet mut user. - Leaving out a field. Every field needs a value. The compiler
reports
error[E0063]and names the missing field. - Using
&strfor struct fields as a beginner. A struct that holds references needs lifetimes, an intermediate topic. UseStringso the struct owns its data.
Practice
Define a struct Book with title: String, author: String, and
pages: u32, deriving Debug. Create one, print a sentence using its
fields, then print the whole struct with {:?}:
The Rust Book by Steve Klabnik, 560 pages
Book { title: "The Rust Book", author: "Steve Klabnik", pages: 560 }
Show solution
#[derive(Debug)]
struct Book {
title: String,
author: String,
pages: u32,
}
fn main() {
let book = Book {
title: String::from("The Rust Book"),
author: String::from("Steve Klabnik"),
pages: 560,
};
println!("{} by {}, {} pages", book.title, book.author, book.pages);
println!("{book:?}");
}