5 exercises — ownership and move semantics, Result vs panic!, Rc vs Arc smart pointers, traits with default implementations, and the borrow checker trade-off in professional English.
0 / 5 completed
Rust-specific English vocabulary reference
ownership — every value has one owner; when owner goes out of scope, value is dropped
lifetime — compiler-tracked duration of a reference's validity
Result<T, E> — recoverable errors; panic! — unrecoverable program errors
trait — shared behaviour interface; can have default method implementations
Arc<Mutex<T>> — thread-safe shared mutable state; Rc<RefCell<T>> — single-thread only
1 / 5
A Rust code review comment says: "This function takes ownership of the String — you should borrow it instead." What does "taking ownership" mean in Rust?
Rust's ownership system is its most distinctive feature. Every value has exactly one owner. When you pass a value to a function without a reference, ownership moves — the original binding becomes invalid.
Move vs. borrow: fn process(s: String) — takes ownership; caller cannot use s afterwards fn process(s: &String) — borrows; caller keeps owning s fn process(s: &mut String) — mutable borrow; caller keeps ownership, function can modify
Why the borrow checker enforces this: It prevents use-after-free and double-free bugs at compile time — no garbage collector needed. If the code compiles, there are no dangling references.
Rust ownership vocabulary: • ownership — a value has exactly one owner (variable/binding) at a time • move — transfer of ownership; original binding invalidated • borrow (&T) — temporary read access without ownership transfer • mutable borrow (&mut T) — temporary exclusive write access • lifetime — compiler-tracked duration of how long a reference is valid • borrow checker — the compiler component that enforces all ownership rules • drop — when the owner goes out of scope, Drop::drop() is called; memory freed
2 / 5
A Rust function signature is: fn divide(a: i32, b: i32) -> Result<i32, String>. A developer from a Java background asks: "Why not throw an exception?" What is the idiomatic Rust answer?
Rust distinguishes between two categories of errors:
1. Recoverable errors → Result<T, E> "File not found", "network timeout", "validation failed" — expected failures that callers should handle. • Ok(value) — success case • Err(error) — failure case • Callers must handle both (or explicitly opt out with unwrap() / expect()) • The ? operator propagates errors up the call chain ergonomically
2. Unrecoverable errors → panic! "Index out of bounds", "assertion failed", programming bugs — situations where the program cannot proceed. Terminates the thread (or process). Not for expected failures.
Why Result over exceptions: • Visibility: the signature -> Result<i32, String> tells callers this can fail • No silent exceptions: no "throws" in the signature that callers can ignore • Exhaustive: match result { Ok(v) => ..., Err(e) => ... } is checked by the compiler
Error handling vocabulary: • unwrap() — get inner value or panic; used in tests, prototypes • expect("message") — like unwrap but with a custom panic message • ? operator — if Err, return Err from current function; if Ok, unwrap value • map_err(), and_then() — transform/chain Results • anyhow, thiserror — popular crates for ergonomic error types
3 / 5
A Rust code review says: "Use Arc<Mutex<T>> here for shared mutable state across threads." But a teammate asks: "What's wrong with just using Rc<RefCell<T>>?" What is the key difference?
Rust enforces thread safety through the Send and Sync marker traits — at compile time, not runtime.
Rc<RefCell<T>> — single-threaded shared mutable state: • Rc<T> — Reference Counted smart pointer. Multiple owners. Not thread-safe (not Send). • RefCell<T> — Interior mutability. Borrow checking at runtime (not compile time). Panics if rules violated. Not thread-safe. • Compiler prevents moving Rc across thread boundaries
Arc<Mutex<T>> — multi-threaded shared mutable state: • Arc<T> — Atomically Reference Counted. Thread-safe (is Send + Sync). Slightly higher cost than Rc. • Mutex<T> — mutual exclusion lock. lock() returns a guard; guard auto-unlocks when dropped. • RwLock<T> — allows multiple readers OR one writer (more permissive than Mutex)
The key insight — compile-time thread safety: If you accidentally try to share Rc across threads, the Rust compiler gives you an error: "Rc<T> cannot be sent between threads safely." No race conditions reach production.
Smart pointer vocabulary: • Box<T> — single owner, heap allocation • Rc<T> — multiple owners, single-threaded, reference counted • Arc<T> — multiple owners, multi-threaded, atomic reference counted • RefCell<T> — runtime borrow checking, single-threaded interior mutability • Mutex<T> — multi-threaded interior mutability with locking • Send — safe to transfer ownership across threads • Sync — safe to share references across threads
4 / 5
A Rust trait definition: trait Animal { fn sound(&self) -> &str; fn describe(&self) -> String { format!("I make this sound: {}", self.sound()) } }. What is significant about the describe method?
Rust traits are Rust's mechanism for shared behaviour — similar to interfaces in Java/C# but more powerful because they can have default method implementations.
Traits with default implementations: • Methods without a body = abstract (must be implemented by each type) • Methods with a body = default implementation (optional to override) • Types get default methods "for free" — no code duplication
This is how Rust's standard library is built. Example: implementing Iterator requires only next() — but you automatically get map(), filter(), collect(), count(), fold(), and 70+ other methods for free.
Trait vs. Java interface: • Java interfaces can have default methods (Java 8+) — similar • But Rust traits also support: associated types, blanket implementations (impl<T: Display> MyTrait for T), and coherence rules
Trait vocabulary: • trait — defines shared behaviour (abstract methods + optional defaults) • implement a trait — impl Animal for Dog { fn sound(&self) -> &str { "woof" } } • trait bound — fn print<T: Display>(val: T) — T must implement Display • trait object — dyn Animal — dynamic dispatch; runtime polymorphism • blanket impl — implement a trait for all types satisfying a constraint • coherence / orphan rule — you can only implement a trait for a type if you own the trait or the type
5 / 5
A team is evaluating Rust for a new service. An engineer says: "The borrow checker rejection rate is high at first, but once we understood it, our code had zero memory-safety bugs in production." Which description of the borrow checker trade-off is most accurate?
The borrow checker is Rust's "shift left" for memory safety — it moves bug detection from runtime (segfault, crash, exploit) to compile time (compiler error).
What the borrow checker eliminates: • Use-after-free — accessing memory after it has been freed (exploitable in C/C++) • Double-free — freeing the same memory twice • Dangling pointer — pointer to memory that has been freed • Data race — two threads accessing the same memory concurrently, one writing • Null pointer dereference — replaced by Option<T> (explicit null handling)
The learning curve trade-off: "Fighting the borrow checker" is a common phrase in the Rust community. Patterns natural in C++ or Java may not compile in Rust — you must structure code around ownership. Initial productivity drops, then rises above the baseline.
Real-world impact: • Microsoft: ~70% of CVEs are memory safety issues — Rust eliminates most categories • Android: memory safety bug rate dropped sharply in code migrated to Rust • AWS, Google, Meta, Linux kernel: all adopting Rust for safety-critical components
Rust community vocabulary: • "fighting the borrow checker" — struggling to restructure code to satisfy ownership rules • fearless concurrency — Rust's promise: write concurrent code without data race fear • zero-cost abstraction — high-level constructs (iterators, traits) compile to efficient machine code • Rustacean — a Rust developer (community self-identifier) • "blazingly fast" — community meme about Rust performance claims • rewrite it in Rust (RIIR) — community enthusiasm for Rust safety/performance