🏗️ Building Your Own LEGO Blocks in Rust: Structs!
Imagine you have a box of LEGO pieces. Each piece has a color, size, and shape. Now, what if you could create your own custom LEGO piece with exactly the parts you want? That’s what structs are in Rust—your personal blueprint for creating custom data pieces!
🎯 What is a Struct?
Think of a struct like a recipe card for a cookie. The card doesn’t have the cookie—it just tells you what ingredients you need:
- Flour ✓
- Sugar ✓
- Eggs ✓
A struct is your recipe. When you follow it, you create an actual cookie (an instance).
// The recipe card (struct definition)
struct Cookie {
flavor: String,
size: u32,
has_chips: bool,
}
📦 Defining Structs
Creating a struct is like designing a form with blank fields to fill in later.
The Basic Recipe
struct Dog {
name: String,
age: u8,
is_fluffy: bool,
}
What just happened?
struct Dog— We named our blueprint “Dog”name: String— Every dog needs a name (text)age: u8— Every dog has an age (small number)is_fluffy: bool— Is it fluffy? Yes or No
graph TD A["struct Dog"] --> B["name: String"] A --> C["age: u8"] A --> D["is_fluffy: bool"] style A fill:#667eea,color:#fff style B fill:#4ECDC4,color:#fff style C fill:#4ECDC4,color:#fff style D fill:#4ECDC4,color:#fff
🎨 Instantiating Structs
Now let’s make an actual dog from our blueprint!
let my_dog = Dog {
name: String::from("Buddy"),
age: 3,
is_fluffy: true,
};
You just created Buddy! 🐕
To read Buddy’s info:
println!("Name: {}", my_dog.name);
// Output: Name: Buddy
To change Buddy’s age (needs mut):
let mut my_dog = Dog {
name: String::from("Buddy"),
age: 3,
is_fluffy: true,
};
my_dog.age = 4; // Happy birthday!
⚡ Field Init Shorthand
Imagine you’re filling a form where your answer is the same as the question. Boring, right? Rust agrees!
The long way:
let name = String::from("Max");
let age = 5;
let dog = Dog {
name: name, // name = name? Repetitive!
age: age, // age = age? Ugh!
is_fluffy: false,
};
The shorthand magic:
let name = String::from("Max");
let age = 5;
let dog = Dog {
name, // Same variable name? Just write it once!
age, // Rust gets it!
is_fluffy: false,
};
✨ When the variable name matches the field name, just write it once!
🔄 Struct Update Syntax
You have a dog. Now you want a copy with just one thing different. Do you rewrite everything? No way!
let dog1 = Dog {
name: String::from("Buddy"),
age: 3,
is_fluffy: true,
};
// Create dog2 - same as dog1, but different age
let dog2 = Dog {
age: 5, // Only this is different
..dog1 // Copy everything else from dog1
};
The ..dog1 is like saying: “For everything I didn’t mention, just copy from dog1!”
⚠️ Important: After using ..dog1, you can’t use dog1.name anymore (ownership moved). But simple types like age and is_fluffy are still okay!
📍 Tuple Structs
Sometimes you want a struct but without naming each field. Like a point on a map—you just need (x, y).
struct Point(i32, i32);
struct Color(u8, u8, u8);
let origin = Point(0, 0);
let red = Color(255, 0, 0);
Access by position (0, 1, 2…):
println!("X: {}", origin.0); // 0
println!("Y: {}", origin.1); // 0
println!("Red value: {}", red.0); // 255
Why use these?
PointandColorare different types- Even though both hold numbers, Rust won’t let you mix them up!
graph LR A["Point"] --> B["#40;x, y#41;"] C["Color"] --> D["#40;r, g, b#41;"] style A fill:#FF6B6B,color:#fff style C fill:#4ECDC4,color:#fff
👻 Unit-Like Structs
What if you need a type with no data at all? Like an empty box that just says “I exist!”
struct Empty;
let e = Empty;
When would you use this?
- As a marker or flag
- When implementing traits (we’ll learn later!)
- To represent a concept without data
Think of it as a name tag with no other info—just “Hi, I’m Empty!”
🔒 Struct Visibility
By default, structs are private. Like a secret recipe that only your kitchen can see.
mod restaurant {
// Public struct - everyone can see it!
pub struct Breakfast {
pub toast: String, // Public field
eggs: u8, // Private field!
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
eggs: 2, // We set eggs internally
}
}
}
}
fn main() {
let meal = restaurant::Breakfast::summer("Wheat");
println!("Toast: {}", meal.toast); // ✅ Works!
// println!("Eggs: {}", meal.eggs); // ❌ Error! eggs is private
}
| Keyword | What it means |
|---|---|
pub struct |
Struct is visible outside |
pub field |
Field is visible outside |
No pub |
Private (hidden) |
graph TD A["pub struct Breakfast"] --> B["pub toast ✅"] A --> C["eggs 🔒"] style A fill:#667eea,color:#fff style B fill:#4ECDC4,color:#fff style C fill:#FF6B6B,color:#fff
🎉 Putting It All Together
// Define our struct
pub struct GameCharacter {
pub name: String,
pub level: u32,
health: u32, // Private!
}
// Tuple struct for position
struct Position(f32, f32);
// Unit-like struct as a marker
struct IsPlayer;
fn main() {
// Create with field init shorthand
let name = String::from("Hero");
let level = 1;
let hero = GameCharacter {
name, // shorthand!
level, // shorthand!
health: 100,
};
// Create another with update syntax
let hero2 = GameCharacter {
name: String::from("Sidekick"),
..hero // Copy level and health
};
// Tuple struct
let pos = Position(10.0, 20.0);
println!("Hero at ({}, {})", pos.0, pos.1);
}
🌟 Key Takeaways
| Concept | What It Does | Example |
|---|---|---|
| Defining Structs | Create a blueprint | struct Dog { name: String } |
| Instantiating | Build from blueprint | Dog { name: "Buddy".to_string() } |
| Field Shorthand | Skip repetition | Dog { name, age } |
| Update Syntax | Copy with changes | Dog { age: 5, ..other_dog } |
| Tuple Structs | Unnamed fields | struct Point(i32, i32) |
| Unit-Like | No fields | struct Marker; |
| Visibility | Control access | pub struct, pub field |
💡 Remember!
Structs are like custom LEGO sets—you design what pieces go together, then build as many as you want!
You’re now ready to organize your data like a pro. Go build something amazing! 🚀
