Glossary
Rust reuses some ordinary words for precise technical meanings — move, borrow, drop — and adds vocabulary with no equivalent in a garbage-collected language, like lifetime or dyn compatible. When a lesson uses a term you’re not sure of, look it up here.
Ownership and memory
Section titled “Ownership and memory”| Term | Meaning |
|---|---|
| Move | Transferring ownership of a value from one binding to another. After let s2 = s1; for a non-Copy type, s1 is no longer valid — using it is a compile error (E0382, “borrow of moved value”), not a runtime bug. See Ownership & Moves. |
Copy | A marker trait for types that are duplicated bit-for-bit on assignment instead of moved — small, stack-only types like integers, bool, and char. A type can derive Copy only if every field is itself Copy, and Copy requires Clone as a supertrait. See Ownership & Moves. |
Clone | A trait for making an explicit, possibly-expensive deep copy via .clone(). Unlike Copy, cloning is always an opt-in method call, so it’s visible at every call site. See Ownership & Moves. |
Shared reference (&T) | A borrow that lets you read a value without taking ownership. Any number of shared references can exist at once, but none of them can mutate through it. See References & Borrowing. |
Exclusive reference (&mut T) | A borrow that allows mutation, with the compiler guaranteeing it is the only active reference (shared or exclusive) to that value for as long as it’s live. See Mutable Borrows. |
| Lifetime | The scope for which a reference is guaranteed valid, written as a generic parameter like 'a. Lifetimes don’t change how long a value lives — they let the compiler check that a reference never outlives what it points to. See Lifetimes. |
| Lifetime elision | The three rules the compiler applies to infer lifetime annotations automatically in common function signatures, so you don’t write 'a everywhere by hand. When the rules can’t determine a unique answer, you annotate explicitly — that’s E0106, “missing lifetime specifier.” See Lifetimes. |
'static | A lifetime meaning “valid for the entire program” — string literals and values with no borrowed data satisfy it. It is not a synonym for “global” or “leaked,” and adding it to silence an unrelated lifetime error usually just moves the error somewhere else. See Lifetimes. |
| Non-Lexical Lifetimes (NLL) | The borrow-checker model (since Rust 2018) where a borrow’s scope ends at its last actual use, not at the end of the enclosing block — so a new borrow can start right after, even inside the same {} block. See Mutable Borrows. |
| Two-phase borrow | A special-cased exclusive borrow (used for things like v.push(v.len())) that is “reserved” while its arguments are still being evaluated and only becomes fully active at the call itself — letting a shared read and the eventual exclusive borrow overlap without tripping E0502. See Mutable Borrows. |
| Reborrow | Creating a new, shorter-lived borrow from an existing reference (&mut *r or an implicit reborrow when passing &mut T to a function) instead of moving the original reference. It’s what lets you use a &mut T more than once without giving it up. See Mutable Borrows. |
| Drop / RAII | ”Resource Acquisition Is Initialization” — a value’s Drop::drop runs automatically when it goes out of scope, in reverse declaration order, freeing memory or releasing a lock/file handle with no manual cleanup call needed. See Ownership & Moves. |
| Slice | A view into a contiguous run of elements (&[T], &str) without owning them. Because it borrows, the compiler rejects any code that would modify or drop the underlying data while the slice is alive. See Slices. |
| Fat pointer | A reference that carries more than one machine word: a slice reference is (pointer, length); a &dyn Trait reference is (pointer, vtable pointer) — 16 bytes on a 64-bit target, versus 8 bytes for an ordinary &T. See Trait Objects. |
| Stack vs heap | Stack allocation is a pointer bump with automatic, scope-based cleanup; heap allocation (via String, Vec<T>, Box<T>) is explicit, sized at runtime, and freed by Drop when its owner goes out of scope. Moving a heap-owning value copies only its stack header (pointer/length/capacity), never the heap data itself. See Ownership & Moves. |
Box<T> | A smart pointer that puts a value on the heap while owning it uniquely — the simplest way to store a value whose size isn’t known until runtime, or to give a value a fixed, small size on the stack regardless of what it contains. See Threads. |
Rc<T> / Arc<T> | Reference-counted smart pointers for shared ownership of a heap value: many owners, freed once the count hits zero. Rc<T> uses a plain, non-atomic counter (single-threaded only); Arc<T> (“Atomically Reference Counted”) uses an atomic counter, which is what makes it safe to clone and send across threads. See Threads. |
RefCell<T> | A type that moves Rust’s borrow rules from compile time to runtime: .borrow()/.borrow_mut() panic (already borrowed: BorrowMutError) instead of failing to compile if the exclusivity rule is violated. See Threads. |
| Interior mutability | Mutating data through a shared (&T) reference by pushing the exclusivity check to runtime instead of compile time — what RefCell<T>, Cell<T>, Mutex<T>, and atomics all provide, each with a different cost and guarantee. See Threads. |
Types and traits
Section titled “Types and traits”| Term | Meaning |
|---|---|
| Struct | A named, fixed-shape data type grouping related values as fields — the closest Rust equivalent to a class, minus inheritance. See Structs. |
| Enum | A type that is exactly one of a fixed set of named variants, each of which can carry its own data. Rust enums are closer to tagged unions than to C-style enums. See Enums. |
| Tuple / unit type | A tuple ((i32, bool)) groups values of different types positionally with no field names. The unit type () is a tuple of zero elements — the implicit return type of a function with no -> ..., similar to void but a real, valid type with exactly one value. See Data Types and Functions. |
Option<T> | An enum with variants Some(T) and None, used everywhere a value might be absent instead of a null reference. The type system forces every caller to handle the None case before reaching the value. See Option. |
Result<T, E> | An enum with variants Ok(T) and Err(E), Rust’s mechanism for recoverable failure. A function that can fail returns Result instead of panicking, and the signature documents exactly what error type it can produce. See Result. |
| Trait | A named set of methods a type can implement, Rust’s mechanism for shared behavior across otherwise-unrelated types — closer to an interface than to a base class. See Traits. |
| Generic | A function, struct, or enum written once and parameterized over a type placeholder (T), so the compiler generates working code for whatever concrete type is used at each call site. See Generics. |
| Trait bound | A constraint on a generic parameter (fn largest<T: PartialOrd>(...)) restricting T to types that implement a given trait, so the compiler allows only the operations that trait guarantees. See Generics. |
impl Trait | Syntax meaning “some concrete type that implements this trait, decided at compile time.” As an argument type it’s sugar for a generic bound; as a return type it lets a function return a closure or iterator without naming its type. See Trait Objects. |
dyn Trait | A trait used as a type on its own, always behind a pointer (&dyn Trait, Box<dyn Trait>). Method calls resolve at runtime through a vtable instead of being decided at compile time, which is what lets values of different concrete types live in the same Vec. See Trait Objects. |
| Dyn compatibility | Whether a trait can be turned into a dyn Trait value at all. Current material may still say “object safety” — that’s the older name for the same requirement (roughly: no generic methods, no returning Self by value). A trait that fails this shows compiler error E0038, “is not dyn compatible,” not “is not object safe.” A where Self: Sized bound can exclude one offending method and make the rest of the trait dyn compatible again. See Trait Objects. |
| Vtable | ”Virtual method table” — the table of function pointers stored alongside a dyn Trait value’s data pointer, used to look up the correct method implementation at runtime. It’s the second word of the fat pointer that makes a trait object work. See Trait Objects. |
| Monomorphization | The compiler generating a separate, fully-specialized copy of a generic function for every concrete type it’s called with, so calls through generics have no runtime dispatch cost — at the price of larger binaries when the same generic is instantiated with many types. See Generics. |
#[derive(...)] | An attribute that generates a trait implementation for a struct or enum automatically — Debug, Clone, Copy, PartialEq, Default, and more — instead of writing the boilerplate by hand. Some derives have preconditions: Copy requires every field to already be Copy (and Clone). See Structs. |
| Orphan rule | The coherence rule that you may implement a trait for a type only if you own the trait, the type, or both — never a foreign trait for a foreign type. It’s what keeps two crates from writing conflicting impls for the same type. See Traits. |
Auto trait (Send / Sync) | A marker trait the compiler implements automatically for a type based on its fields, with no impl block written by hand. Send means a value can move to another thread; Sync means a shared reference to it can. Rc<T> and RefCell<T> are neither, so sharing them across threads is E0277 at compile time, not a runtime data race. See Threads. |
Sized | A marker trait for types whose size is known at compile time — the default bound on every generic type parameter. A dyn Trait value is not Sized (its concrete type varies), which is why it must always sit behind a pointer. See Trait Objects. |
| Associated type | A placeholder type declared inside a trait (type Item; in Iterator) and fixed by each implementor, rather than supplied by the caller like a generic parameter. It keeps a trait to one implementation per type instead of many. See Traits. |
| Newtype | A tuple struct with exactly one field (struct Meters(f64);) used to wrap an existing type in a distinct, named type — two newtypes over the same underlying type are not interchangeable, unlike a plain type alias. See Structs. |
Errors
Section titled “Errors”| Term | Meaning |
|---|---|
| Panic | Rust’s response to an unrecoverable bug: it unwinds the call stack, runs destructors along the way, and terminates the current thread (the whole process, in a single-threaded program). The panic hook prints thread 'main' (<id>) panicked at file:line:col: ... before that happens. See Panic. |
| Unwinding vs aborting | Two ways a panic can end a thread: unwinding walks back up the stack running every Drop along the way (the default, panic = "unwind"); aborting kills the process immediately with none of that cleanup (panic = "abort"), which is faster and produces a smaller binary but skips destructors. See Panic. |
? operator | Sugar for “return early with Err/None if this expression is one, otherwise unwrap it.” It only works in a function returning Result or Option, and it calls From::from automatically to convert between error types along the way. See The ? Operator. |
From | A conversion trait (From<A> for B) that lets ? turn one error type into another automatically, as long as the conversion is implemented once. It’s the mechanism, not ? itself, that does the type conversion. See Custom Errors. |
Box<dyn Error> | A type-erased error box that can hold any type implementing std::error::Error — convenient in a binary’s main, where you don’t care which concrete error type failed, but a poor choice for a library’s public API because callers lose the ability to match on a specific error variant. See Custom Errors. |
Error::source | An optional method on the Error trait that exposes the underlying cause of an error, letting callers (or a debug print) walk a chain of nested failures back to the root cause. See Custom Errors. |
| Poisoning | What happens to a Mutex<T>/RwLock<T> when the thread holding its lock panics while holding it: the lock becomes “poisoned,” and every later .lock()/.read()/.write() returns an Err (which the common .unwrap() pattern turns straight into a second panic) rather than silently handing out possibly-inconsistent data. See Threads. |
unwrap() / expect() | Methods that extract the Ok/Some value or panic on Err/None — expect(msg) adds a custom message. Fine in a quick script or a test; in production code past the prototype stage, each one is a spot that turns a recoverable error into a crash. See Result. |
Iterators and closures
Section titled “Iterators and closures”| Term | Meaning |
|---|---|
| Laziness | An iterator adapter chain (.map(...).filter(...)) does no work when you build it — it only describes a transformation. Nothing runs until a consumer drives the chain, which is what makes long chains as cheap as a single hand-written loop. See Iterators. |
| Adapter vs consumer | An adapter (map, filter, zip, enumerate, …) turns one iterator into another, lazily. A consumer (collect, sum, fold, a for loop) pulls values through the whole chain and produces a final, concrete result. See Iterators. |
collect() | The most general consumer: it gathers an iterator’s items into a collection, with the target type inferred from context or spelled out with turbofish. .collect::<Result<Vec<_>, _>>() even short-circuits on the first Err. See Iterators. |
| Turbofish | The ::<T> syntax (.collect::<Vec<i32>>()) used to spell out a generic type explicitly when the compiler can’t infer it from context alone. See Iterators. |
Fn / FnMut / FnOnce | The three closure traits, describing how a closure uses what it captured: Fn only borrows (&T) and can be called any number of times, FnMut borrows mutably (&mut T), and FnOnce takes ownership (T) and can be called only once. Every closure implements at least FnOnce. See Closures. |
| Capture | The set of environment variables a closure references in its body, which the compiler borrows (or moves, with move) into the closure’s generated struct — automatically choosing the least restrictive mode (Fn over FnMut over FnOnce) each variable’s usage allows. See Closures. |
move closures | The move keyword forces a closure to take ownership of every captured variable, regardless of whether the body only reads it. Needed whenever the closure must outlive the scope it was created in — most commonly, one handed to thread::spawn. See Closures. |
Concurrency
Section titled “Concurrency”| Term | Meaning |
|---|---|
| Thread | An OS-level thread with its own stack, created with thread::spawn. JoinHandle::join() blocks the calling thread until it finishes and returns its result (or the panic payload, if it panicked). See Threads. |
| Scoped thread | A thread whose lifetime is provably bounded to an enclosing thread::scope block, which lets it borrow local, non-'static data safely — unlike a plain thread::spawn, which requires everything it captures to be 'static or owned. See Threads. |
Mutex<T> / RwLock<T> | Interior-mutability types that add runtime mutual exclusion: Mutex<T> allows exactly one lock holder at a time (read or write); RwLock<T> allows either many simultaneous readers or one writer. Both are combined with Arc<T> to share mutable state across threads. See Threads. |
Channel / sync_channel | std::sync::mpsc gives you a multi-producer, single-consumer channel: sending a value moves its ownership to whichever thread eventually receives it, which is why channels are race-free by construction. mpsc::channel() is unbounded; mpsc::sync_channel(n) caps the buffer at n and makes send block once it’s full — deliberate backpressure instead of unbounded memory growth. See Channels. |
| Atomic | A type in std::sync::atomic (or Arc<T>’s internal reference count) that supports lock-free, indivisible read-modify-write operations directly on the hardware — cheaper than a Mutex for a single counter or flag, but it doesn’t generalize to protecting more than one value at a time. See Threads. |
| Data race vs race condition | A data race is a compile-time impossibility in safe Rust — two threads accessing the same memory with at least one write and no synchronization, ruled out by Send/Sync. A race condition is a logic bug (e.g. two operations that are individually safe but produce a wrong result depending on their order) that the type system does not and cannot catch. See Concurrency, Testing & Cargo. |
async / Future / executor / .await | An async fn doesn’t run its body when called — it returns a Future, a value representing work that hasn’t completed yet. .await suspends the current async fn until that future resolves, without blocking the OS thread. An executor (such as tokio) is the runtime that actually polls futures to completion; without one, an async fn’s body never runs at all. See Async Introduction. |
Pin | A wrapper the compiler needs to safely poll a Future, because an async fn’s compiler-generated state machine can contain self-references that must not move in memory once polling starts. You’ll meet it mostly through .await and generated code rather than writing it by hand this early. See Async Introduction. |
Toolchain
Section titled “Toolchain”| Term | Meaning |
|---|---|
| Cargo | Rust’s build tool and package manager: cargo build, cargo run, cargo test, and dependency management all go through it instead of a separate build script per project. See Cargo & Tests. |
| Crate | A compilation unit — either a binary (has a main) or a library. What most other languages call a “package,” Cargo calls a crate; a single Cargo project can define one library crate plus multiple binaries. See Cargo & Tests. |
| Edition | The edition key in Cargo.toml ("2024", "2021", …) selects which syntax and default-behavior dialect a crate compiles under, letting the language evolve without breaking crates that don’t opt in. Crates on different editions still link together normally. See Cargo & Tests. |
Cargo.lock | A file pinning the exact resolved version of every dependency, direct and transitive, so a build is reproducible across machines. Commit it for binaries; for a library it’s typically not committed, since downstream crates resolve their own versions. See Cargo & Tests. |
rust-version / MSRV | rust-version in Cargo.toml declares a crate’s Minimum Supported Rust Version. MSRV-aware dependency resolution is a separate, opt-in Cargo mechanism controlled by resolver.incompatible-rust-versions (default "fallback" since Cargo 1.84) — not by edition, which is unrelated to MSRV entirely. See Cargo & Tests. |
| Clippy | cargo clippy — Rust’s official lint tool, running far more checks than the compiler itself, including many that flag exactly the smells in the table below (clippy::redundant_clone, clippy::unwrap_used, and others). See Cargo & Tests. |
| Rustfmt | cargo fmt — the standard formatter that rewrites source files to one canonical style, removing formatting bikeshedding from code review entirely. See Cargo & Tests. |
| Feature | A named, optional piece of a crate’s functionality declared under [features] in Cargo.toml, enabled per dependency (crate = { version = "1", features = ["x"] }) so consumers pay only for the code paths they actually use. See Cargo & Tests. |
| Workspace | A [workspace] in a root Cargo.toml that groups multiple crates sharing one Cargo.lock and one target directory — Cargo’s answer to a monorepo. See Cargo & Tests. |
cargo test attributes | #[test] marks a function as a test case; #[should_panic(expected = "...")] asserts it panics with a matching message (omit expected and it passes on any panic, not just the intended one); #[ignore] skips it by default; cargo test -- --nocapture shows println! output even from passing tests. See Cargo & Tests. |
Reading AI-generated code: smells to look for
Section titled “Reading AI-generated code: smells to look for”Generated Rust compiles far more often than it’s actually idiomatic or correct under edge cases — these are the patterns most likely to satisfy the borrow checker while quietly hiding a design problem.
| Pattern | Why it’s a smell | Covered in |
|---|---|---|
.clone() to silence the borrow checker | The compiler’s own suggested fix for a move error is often .clone() — but reaching for it reflexively, instead of asking whether the code should have borrowed (&x) in the first place, hides a design issue behind a real (if small) runtime cost. | Ownership & Moves |
unwrap() / expect() sprinkled everywhere | Generated code often treats every Result/Option the same way: unwrap it and move on. Past a quick script, each unwrap() is a crash waiting on input the happy path didn’t anticipate, where a match, ?, or a real default would recover instead. | Result |
String parameter where &str fits | Taking s: String forces every caller to allocate or move an owned string in, even when the function only ever reads it. &str accepts both a &String (via automatic coercion) and a string literal, so it’s the more flexible parameter type. | Vec & String |
&String / &Vec<T> parameter instead of &str / &[T] | A reference to the owned type forces the caller to have a String/Vec<T> at all, when the function only needs to read a slice — a &str/&[T] accepts a slice of anything, including a literal or an array, via automatic coercion. | Slices |
Rc<RefCell<T>> as a first resort | Reaching for shared, mutable, reference-counted state before checking whether ordinary ownership and borrowing (or, across threads, Arc<Mutex<T>> or a channel) would do the job — it compiles, but it opts out of the compile-time guarantees that make Rust’s ownership model worth using. | Threads |
Catch-all _ => arm on an enum match | A _ arm compiles cleanly against today’s variants but silently “handles” any variant added later — including ones that should have gotten their own explicit logic. Matching every variant by name (or marking the enum #[non_exhaustive] deliberately) turns a future missed case into a compile error (E0004) instead of silent wrong behavior. | Pattern Matching |
| Byte-slicing text with a hardcoded range | &s[0..1] indexes into a String’s UTF-8 bytes, not its characters — for any non-ASCII text this panics with “byte index … is not a char boundary” the moment a multi-byte character sits across the cut point. .chars(), .char_indices(), or a Unicode-aware crate avoid it entirely. | Vec & String |
'static slapped on to silence a lifetime error | Adding 'static to a reference or bound because the compiler was complaining, without understanding why, either fails to compile for an unrelated reason or forces the caller to only ever pass in data that lives for the whole program — usually not what was actually needed. | Lifetimes |
Box<dyn Error> as a library’s public return type | Convenient in a binary’s main, but in a library API it erases the caller’s ability to match on which specific failure occurred — a concrete error enum implementing Error (with From impls for each source error) keeps that information intact. | Custom Errors |
#[should_panic] with no expected = "..." | A bare #[should_panic] passes as long as the function panics at all — including from a completely unrelated bug — instead of proving it panics for the reason the test claims to check. | Cargo & Tests |