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
134: The Drop Trait and RAII in Rust
I've noticed a recurring pattern when developers transition from C++ or manual memory management languages to Rust: they treat std::mem::drop like a magic "delete" button. They think that by calling drop(my_variable), they are explicitly invoking the destructor logic to free memory exactly at that millisecond, similar to calling free() or delete.
Thinking drop() is a method you can call on demand
Here is the common mistake. A learner will try to do something like this:
let mut data = String::from("Important Data");
// ... some code ...
data.drop(); // Error: no method named `drop` found for struct `String`
When they see that error, they usually pivot to std::mem::drop(data). While that "works" to make the variable go away, the misconception persists: they believe they've called a special function that triggers the Drop trait. They think they are manually managing the lifecycle of the object.
The Reality: Moving Values into Oblivion
Here is the truth: std::mem::drop is not a special function with internal magic. It is actually a very simple, generic function that does... absolutely nothing. Take a look at its approximate implementation in the standard library:
pub fn drop<T>(_x: T) {}
That's it. It takes ownership of _x` and then the function ends. Because _x` now owns the value and is going out of scope at the closing brace, Rust's ownership rules trigger the actual cleanup. You aren't "calling" the destructor; you are simply moving the value into a scope where it is forced to die.
This is the essence of RAII (Resource Acquisition Is Initialization). In Rust, the "Initialization" part is simple, but the "Resource Release" part is handled automatically by the compiler. You don't manage memory; you manage ownership. When the owner dies, the resource dies.
Implementing Your Own Cleanup Logic with the Drop Trait
Most of the time, you'll rely on the standard library's implementations (like Vec or File). But sometimes you need your own custom cleanup—like closing a network socket, releasing a database lock, or deleting a temporary file.
To do this, you implement the Drop trait. I like to think of drop as the "final will and testament" of your struct.
struct DatabaseConnection {
connection_id: u32,
}
impl Drop for DatabaseConnection {
fn drop(&mut self) {
println!("Closing connection {}. Sending logout signal to server...", self.connection_id);
// In a real app, you'd put your actual cleanup logic here
}
}
fn main() {
{
let conn = DatabaseConnection { connection_id: 42 };
println!("Doing work with connection 42...");
} // <--- conn goes out of scope here, and `drop()` is called automatically
println!("Connection should be closed by now.");
}
One critical rule to remember: you cannot call drop() manually on a type that implements the Drop trait. If you try to call conn.drop(), the compiler will stop you. Why? Because if you could call it manually, and then the variable went out of scope, Rust would try to drop it again, leading to a double-free vulnerability. Rust prevents this by design.
The Order of Operations Matters
When you have a complex struct with multiple fields, Rust drops them in the order they were declared. This is a subtle point, but it can save you from some weird bugs when you're dealing with interdependent resources.
If you have a struct with a File and a Buffer, and the Buffer depends on the File being open, you must declare the File first. Rust will drop the fields from top to bottom. If you reverse them, you might accidentally close the file before the buffer has a chance to flush its final bytes.
- Fields: Dropped in declaration order.
- Collections: Elements in a
Vecare dropped in the order they appear in the vector. - Scopes: Variables are dropped in the reverse order of their creation.
I've spent way too many hours debugging "use-after-free" style logic in other languages; having the compiler handle the cleanup sequence based on declaration order is honestly one of my favorite parts of the language.
📋 Practical Task
Exercise: Implementing a Scoped Session Logger
You are building a system that needs to track how long a user session remains active. Instead of relying on the developer to remember to call a logout() function, you will implement an RAII guard that automatically logs the session end when the guard is dropped.
Requirements:
- Create a struct named
SessionGuardthat holds ausername: Stringand astart_time: std::time::Instant. - Implement the
Droptrait forSessionGuard. Inside thedropmethod, calculate the elapsed time sincestart_timeand print a message:"Session for [username] ended after [duration] seconds." - In your
mainfunction, create a scope (using curly braces{ }). - Inside that scope, instantiate a
SessionGuard. - Simulate some work by calling
std::thread::sleep(std::time::Duration::from_secs(2)). - Ensure that the "Session ended" message prints automatically when the scope closes, without you ever calling a cleanup function explicitly.
There are no comments for now.