Rust Structs Explained: Update Syntax, Tuple Structs, derive(Debug)
November 10, 2026
Welcome to episode 10 of Rust for Beginners, a series for complete beginners (no programming experience needed). In the last video, we learned about slices. Today, we'll learn how to create our own types with structs. Every code block below is the exact code from the video, ready to copy.
- All episodes are in the Rust Tutorial for Beginners playlist.
- Versions used: Rust 1.99.1 on Ubuntu, VS Code with rust-analyzer.
1. New project
Let's create a new project called structs, and open it in VS Code.
cargo new structs
cd structs2. Define
A struct groups related values together. We write struct, a name in CamelCase, and its fields, each with a type. Our User has a name, an email, and an age.
src/main.rs:
struct User {
name: String,
email: String,
age: u32,
}
fn main() {
let user1 = User {
name: String::from("Alice"),
email: String::from("alice@example.com"),
age: 30,
};
user1.age = 31;
println!("{} ({}) is {} years old.", user1.name, user1.email, user1.age);
}Error in step 6:
error[E0594]: cannot assign to `user1.age`, as `user1` is not declared as mutable
--> src/main.rs:14:5
|
14 | user1.age = 31;
| ^^^^^^^^^^^^^^ cannot assign
|
help: consider changing this to be mutable
|
8 | let mut user1 = User {
| +++Then line 8 becomes let mut user1 = User {. Output: Alice (alice@example.com) is 31 years old.
3. Create
To create a User, we write its name and give every field a value.
4. Access
We read a field with a dot: user one dot name.
5. Change
Let's change the age to thirty-one, and run it.
cargo run6. Error
Error: user one is not mutable. In Rust, the whole struct is either mutable or not. Let's add mut, as the compiler suggests.
7. Run A
Now it works.
cargo run8. Builder fn
Often, a function builds the struct for us. When a parameter has the same name as a field, we can write it just once. That's the field init shorthand.
struct User {
name: String,
email: String,
age: u32,
}
fn main() {
let user1 = new_user(String::from("Alice"), String::from("alice@example.com"));
let user2 = User {
name: String::from("Bob"),
..user1
};
println!("{} is {}, email {}", user2.name, user2.age, user2.email);
}
fn new_user(name: String, email: String) -> User {
User { name, email, age: 18 }
}Output: Bob is 18, email alice@example.com
9. Update syntax
To make a new User from an old one, we use dot dot. Bob gets his own name, and the other fields come from user one.
10. Run B
Let's run it. Bob is eighteen, with Alice's email.
cargo run11. Print
What if we want to print a whole struct? Let's try colon question mark.
cargo runstruct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 3, y: 7 };
println!("{p:?}");
}Error:
error[E0277]: `Point` doesn't implement `Debug`
--> src/main.rs:8:15
|
8 | println!("{p:?}");
| ^^^^^ `Point` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
= help: the trait `Debug` is not implemented for `Point`
help: consider annotating `Point` with `#[derive(Debug)]`
|
1 + #[derive(Debug)]
2 | struct Point {12. derive
Rust doesn't know how to show a Point yet. But it tells us the fix: add derive Debug above the struct.
13. Pretty
With hash colon question mark, we get a pretty, multi-line version.
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
struct Color(u8, u8, u8);
fn main() {
let p = Point { x: 3, y: 7 };
println!("{p:?}");
println!("{p:#?}");
println!("x + y = {}", p.x + p.y);
let red = Color(255, 0, 0);
println!("Red: {}, {}, {}", red.0, red.1, red.2);
}Output:
Point { x: 3, y: 7 }
Point {
x: 3,
y: 7,
}
x + y = 10
Red: 255, 0, 014. Tuple struct
There are also tuple structs. Their fields have no names, only positions: zero, one, two.
15. Run C
Let's run it.
cargo runRecap
To recap: a struct groups named fields. Use a dot to read them, and mut to change them. Field init shorthand and update syntax save typing. And derive Debug lets us print a struct.