Skip to content

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.

TermMeaning
MoveTransferring 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.
CopyA 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.
CloneA 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.
LifetimeThe 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 elisionThe 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.
'staticA 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 borrowA 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.
ReborrowCreating 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.
SliceA 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 pointerA 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 heapStack 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 mutabilityMutating 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.
TermMeaning
StructA named, fixed-shape data type grouping related values as fields — the closest Rust equivalent to a class, minus inheritance. See Structs.
EnumA 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 typeA 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.
TraitA 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.
GenericA 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 boundA 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 TraitSyntax 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 TraitA 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 compatibilityWhether 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.
MonomorphizationThe 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 ruleThe 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.
SizedA 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 typeA 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.
NewtypeA 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.
TermMeaning
PanicRust’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 abortingTwo 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.
? operatorSugar 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.
FromA 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::sourceAn 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.
PoisoningWhat 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.
TermMeaning
LazinessAn 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 consumerAn 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.
TurbofishThe ::<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 / FnOnceThe 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.
CaptureThe 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 closuresThe 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.
TermMeaning
ThreadAn 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 threadA 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_channelstd::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.
AtomicA 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 conditionA 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 / .awaitAn 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.
PinA 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.
TermMeaning
CargoRust’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.
CrateA 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.
EditionThe 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.lockA 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 / MSRVrust-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.
Clippycargo 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.
Rustfmtcargo fmt — the standard formatter that rewrites source files to one canonical style, removing formatting bikeshedding from code review entirely. See Cargo & Tests.
FeatureA 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.
WorkspaceA [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.

PatternWhy it’s a smellCovered in
.clone() to silence the borrow checkerThe 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 everywhereGenerated 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 fitsTaking 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 resortReaching 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 matchA _ 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 errorAdding '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 typeConvenient 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