Chapter 28: Rust Structs
Rust structs — the real workhorse for creating your own custom data types.
In Rust, when you want to group related data together with names, with meaning, and usually with behavior (methods), you use structs. This is where your program starts to feel like real software engineering — not just playing with numbers and strings.
Let me explain Rust structs like your patient teacher sitting next to you with VS Code open: slowly, clearly, with many small-to-medium examples you can copy-paste and run right now, Hyderabad-flavored analogies, comparisons to other languages, and building from very simple → medium → advanced patterns.
1. What is a Struct? (The Core Idea)
A struct (short for structure) lets you create a named collection of fields — each field has a name and a type.
- Fields have meaningful names (unlike tuples which only have positions .0, .1)
- You can add methods to structs (via impl)
- Rust has three kinds of structs (you’ll use the first one 95% of the time):
| Kind | Syntax | When to use | Fields named? | Tuple-like? |
|---|---|---|---|---|
| Classic / Named | struct Person { name: String, … } | Most common — normal data with names | Yes | No |
| Tuple struct | struct Point(i32, i32); | When order matters, no need for names | No | Yes |
| Unit struct | struct Empty; | Marker / zero-sized type (rare) | No fields | — |
We’ll focus mostly on classic named structs — that’s what you’ll write every day.
2. Defining & Creating a Classic Struct
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// Definition struct Person { name: String, age: u32, is_active: bool, city: String, // can be any type } fn main() { // Creation — field init shorthand (very common) let name = String::from("Webliance"); let city = String::from("Hyderabad"); let me = Person { name, // same as name: name, age: 25, is_active: true, city, // same as city: city, }; println!("{} is {} years old from {}", me.name, me.age, me.city); } |
- Fields are public by default (no pub needed inside same module)
- Order of fields in initialization does not matter — Rust matches by name
- Field init shorthand: if variable name == field name → just write the name
3. Accessing & Mutating Fields
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
fn main() { let mut person = Person { name: String::from("Rahul"), age: 28, is_active: false, city: String::from("Secunderabad"), }; // Read println!("Original: {} from {}", person.name, person.city); // Mutate (needs mut!) person.age += 1; person.city = String::from("Hyderabad"); println!("Updated: {} is now {} from {}", person.name, person.age, person.city); } |
- .field_name syntax (dot access)
- Struct must be mut to change any field
4. Adding Methods with impl (This is Where Structs Become Powerful)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
struct Rectangle { width: f64, height: f64, } impl Rectangle { // Method — takes &self (immutable borrow) fn area(&self) -> f64 { self.width * self.height } // Method with mutable borrow fn scale(&mut self, factor: f64) { self.width *= factor; self.height *= factor; } // Associated function (static method — no self) fn square(size: f64) -> Rectangle { Rectangle { width: size, height: size, } } } fn main() { let mut rect = Rectangle { width: 30.0, height: 50.0 }; println!("Area: {:.2}", rect.area()); // calls method rect.scale(1.5); println!("After scale: {} × {}", rect.width, rect.height); // Call associated function on type let sq = Rectangle::square(10.0); println!("Square area: {}", sq.area()); } |
- &self = immutable borrow (read-only method)
- &mut self = mutable borrow (changes the struct)
- self = takes ownership (consumes the struct — rare)
- No self = associated function (like static method — call on Type::name())
5. Struct Update Syntax (Very Convenient)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
fn main() { let base = Person { name: String::from("Priya"), age: 24, is_active: true, city: String::from("Warangal"), }; // Create new person based on base, only change some fields let updated = Person { age: 25, // override city: String::from("Hyderabad"), ..base // copy all other fields }; println!("Updated: {} is {} from {}", updated.name, updated.age, updated.city); } |
- ..base copies remaining fields from base
- Very useful for builder-like patterns or small modifications
6. Tuple Structs (When Names Aren’t Needed)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
struct Color(i32, i32, i32); // RGB struct Point(f64, f64); // 2D point fn main() { let red = Color(255, 0, 0); let origin = Point(0.0, 0.0); println!("Red: ({}, {}, {})", red.0, red.1, red.2); // Destructuring works too let Color(r, g, b) = red; println!("R={}, G={}, B={}", r, g, b); } |
Use when the meaning is obvious from order (colors, coordinates, vectors).
7. Unit Structs (Rare but Useful)
|
0 1 2 3 4 5 6 7 8 9 10 11 |
struct AlwaysSunny; fn main() { let weather = AlwaysSunny; // zero size, zero cost, used as marker/type-level flag } |
Common in traits/impl patterns or zero-sized types.
Teacher Summary Table
| Feature | Classic struct | Tuple struct | Unit struct |
|---|---|---|---|
| Syntax | struct Name { field: Type } | struct Name(Type, Type); | struct Name; |
| Fields named? | Yes | No (positions only) | No fields |
| Methods possible? | Yes | Yes | Yes |
| Typical use | Normal data records | Simple wrappers (Color, Point) | Markers, flags |
| Readability | Excellent (named fields) | OK for small fixed tuples | — |
Practice Challenge for Today
Create cargo new rust_structs and try:
- struct User { username: String, email: String, active: bool, sign_in_count: u64 }
- Add method increment_sign_in(&mut self)
- Add associated function new(username: String, email: String) -> User
- Use struct update syntax to create a deactivated copy
- Create tuple struct Coordinates(f64, f64) + method distance_from_origin(&self)
Want to go deeper next?
- Methods vs associated functions in detail?
- Structs + ownership/borrowing (very important with String fields)?
- Enums (the other big custom type — often used with structs)?
- Or impl blocks, traits on structs?
Just tell me — your Rust teacher from Hyderabad is right here! 🦀🚀
