Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
21: Structs and Methods
Imagine you're designing a digital camera. A camera isn't just one piece of information; it's a collection of related states. It has an ISO setting, a shutter speed, a battery percentage, and maybe a filename for the next shot. If you tried to track all these as separate variables in your main loop, your code would quickly become a tangled mess of arguments being passed from function to function.
In Rust, a struct is like the physical body of that camera. It lets you bundle all those related pieces of data into a single, named package. But a camera that just sits there holding data isn't very useful. You need buttons to press—actions like "take photo" or "change ISO." That's where methods come in. Methods are functions that "live" inside the struct's implementation, allowing the object to act upon its own data.
Bundling your data together
Let's move away from cameras for a second and look at something we'll actually code. Suppose we're building a game. Instead of having a player_name string and a player_health integer floating around, we'll wrap them in a struct. I prefer this approach because it makes the intent of the code immediately clear to anyone else reading it.
struct Player {
name: String,
health: i32,
level: u32,
}
Now, whenever you need a player, you just create one instance of Player. It's much cleaner than managing three separate variables. I've found that the moment you start grouping data this way, your functions become much simpler because you're passing one "thing" instead of a dozen individual parameters.
Giving your data some behavior
Defining the data is the easy part. The real power comes when we use an impl block. This is where we define the methods that the Player can perform. Unlike the struct definition, which just lists the fields, the impl block is where the logic lives.
impl Player {
// This is a method. Note the '&self' argument.
fn shout_name(&self) {
println!("I am {}!", self.name);
}
// This method modifies the player, so we need '&mut self'
fn take_damage(&mut self, amount: i32) {
self.health -= amount;
println!("Ouch! Health is now {}", self.health);
}
}
Notice that shout_name uses &self. This tells Rust that the method is just borrowing the data to read it. If you want to change a value—like lowering health—you must use &mut self. If you forget the mut, Rust will stop you right there with a compiler error. It's a bit annoying at first, but it saves you from those nightmare bugs where data changes when you didn't expect it to.
The magic of the self keyword
You've probably noticed self appearing everywhere. In Rust, self is how a method accesses the specific instance it was called on. When you call my_player.shout_name(), Rust automatically passes my_player as the first argument to the function.
I should also mention "associated functions." These are functions inside an impl block that don't take self as a parameter. We typically use these as constructors. Since you can't create a struct instance out of thin air without providing all the fields, we usually write a new function to handle the setup logic for us:
impl Player {
fn new(name: String) -> Self {
Self {
name,
health: 100,
level: 1,
}
}
}
// Now we use it like this:
let mut hero = Player::new(String::from("Althea"));
hero.take_damage(20);
Using Self (with a capital S) is just a shorthand for the name of the struct. It makes your code easier to maintain; if you ever rename Player to Character, you don't have to hunt through every single function to update the return types.
📋 Practical Task
Build a Digital Inventory System
Your task is to create a system for managing an RPG inventory. You need to ensure the system can track items and handle the logic of adding or removing them without letting the inventory count go below zero.
Requirements:
- Create a struct named
Inventorythat tracksowner(a String) anditem_count(an integer). - Implement a
newassociated function to initialize a new inventory with a name and 0 items. - Implement a method
add_itemthat increases theitem_countby 1. - Implement a method
remove_itemthat decreases theitem_countby 1, but only if the count is greater than 0. If it's already 0, it should print "Inventory is empty!" and not subtract anything. - In your
mainfunction, create an instance ofInventory, add a few items, remove some, and attempt to remove more items than you have to test your logic.
There are no comments for now.