What you'll learn: Course structure, the interactive format, and how familiar C/C++ concepts map to Rust equivalents. This chapter sets expectations and gives you a roadmap for the rest of the book.
Want to skip straight to code? Jump to Show me some code
Whether you're coming from C or C++, the core pain points are the same: memory safety bugs that compile cleanly but crash, corrupt, or leak at runtime.
shared_ptr, unique_ptr, RAII, and move semantics are steps in the right direction, but they are bandaids, not cures — they leave use-after-move, reference cycles, iterator invalidation, and exception safety gaps wide open📖 Deep dive: See Why C/C++ Developers Need Rust for concrete vulnerability examples, the complete list of what Rust eliminates, and why C++ smart pointers aren't enough
Drop trait is RAII done right — the compiler automatically frees resources when they go out of scope, and prevents use-after-move which C++ RAII cannotResult<T, E>), making error handling explicit and visible in the type signature// Rust equivalent of erase-during-iteration: retain()
pending_faults.retain(|f| f.id != fault_to_remove.id);
// Or: collect into a new Vec (functional style)
let remaining: Vec<_> = pending_faults
.into_iter()
.filter(|f| f.id != fault_to_remove.id)
.collect();
Send and Sync traitsfn safe_rust_ownership() {
// Move is destructive: original is gone
let data = vec![1, 2, 3];
let data2 = data; // Move happens
// data.len(); // Compile error: value used after move
// Borrowing: safe shared access
let owned = String::from("Hello, World!");
let slice: &str = &owned; // Borrow — no allocation
println!("{}", slice); // Always safe
// No dangling references possible
/*
let dangling_ref;
{
let temp = String::from("temporary");
dangling_ref = &temp; // Compile error: temp doesn't live long enough
}
*/
}
Box<T> Heap Allocation Visualizationfn box_allocation_example() {
// Stack allocation
let stack_value = 42;
// Heap allocation with Box
let heap_value = Box::new(42);
// Moving ownership
let moved_box = heap_value;
// heap_value is no longer accessible
}
fn slice_operations() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
let full_slice = &data[..]; // [1,2,3,4,5,6,7,8]
let partial_slice = &data[2..6]; // [3,4,5,6]
let from_start = &data[..4]; // [1,2,3,4]
let to_end = &data[3..]; // [4,5,6,7,8]
}
Send/Sync checking)std::move which leaves zombie objects)Drop trait = RAII done right, no Rule of Five neededMutex<T> wraps the data, not the access)Result<T, E>), visible in function signatures, propagated with ?cargo replaces make/CMake + lint + test frameworks| Concept | C | C++ | Rust | Key Difference |
|---|---|---|---|---|
| Memory management | malloc()/free() | unique_ptr, shared_ptr | Box<T>, Rc<T>, Arc<T> | Automatic, no cycles |
| Arrays | int arr[10] | std::vector<T>, std::array<T> | Vec<T>, [T; N] | Bounds checking by default |
| Strings | char* with \0 | std::string, string_view | String, &str | UTF-8 guaranteed, lifetime-checked |
| References | int* ptr | T&, T&& (move) | &T, &mut T | Borrow checking, lifetimes |
| Polymorphism | Function pointers | Virtual functions, inheritance | Traits, trait objects | Composition over inheritance |
| Generic programming | Macros (void*) | Templates | Generics + trait bounds | Better error messages |
| Error handling | Return codes, errno | Exceptions, std::optional | Result<T, E>, Option<T> | No hidden control flow |
| NULL/null safety | ptr == NULL | nullptr, std::optional<T> | Option<T> | Forced null checking |
| Thread safety | Manual (pthreads) | Manual synchronization | Compile-time guarantees | Data races impossible |
| Build system | Make, CMake | CMake, Make, etc. | Cargo | Integrated toolchain |
| Undefined behavior | Runtime crashes | Subtle UB (signed overflow, aliasing) | Compile-time errors | Safety guaranteed |