Rust Glossary
A reference guide to Rust terminology — from core concepts to advanced topics — arranged alphabetically. Each entry gives a concise definition and, where helpful, a short code example.
Arc
Arc<T> (Atomically Reference Counted) is a smart pointer that allows multiple
owners to share the same heap-allocated value across threads. Unlike Rc<T>,
it uses atomic operations to update the reference count, making it safe to
clone() and send to another thread. It is always immutable on its own; pair it
with Mutex<T> or RwLock<T> for shared mutable state.
use std::sync::Arc;
let shared = Arc::new(vec![1, 2, 3]);
let clone = Arc::clone(&shared);
std::thread::spawn(move || println!("{:?}", clone)).join().unwrap();Borrow Checker
The borrow checker is the part of the Rust compiler that enforces ownership and borrowing rules at compile time. It tracks every reference in your program and rejects code that would cause a use-after-free, a data race, or an invalidated reference. If your code compiles, the borrow checker has verified that none of these memory safety bugs can occur at runtime.
Box
Box<T> is the simplest heap-allocating smart pointer. It moves a value of type
T onto the heap and keeps a single owning pointer to it on the stack. When the
Box is dropped the heap memory is freed automatically. Common uses are
recursive data structures (e.g. linked lists), large values you want to avoid
copying, and returning trait objects (Box<dyn Trait>).
let b = Box::new(5);
println!("b = {}", b); // dereferences automatically via DerefCargo
Cargo is Rust's official build system and package manager. It handles compiling
crates, managing dependencies declared in Cargo.toml, running tests, formatting
code, generating documentation, and publishing packages to crates.io. Almost
every Rust project is a Cargo project; the commands cargo build, cargo test,
and cargo run are the daily workhorses.
Clone
Clone is a trait that allows a type to produce an explicit deep copy of itself
via the .clone() method. Unlike Copy, cloning is always explicit and may be
expensive (e.g. copying a heap-allocated String or Vec). You can derive it
automatically with #[derive(Clone)] if all fields are Clone.
let s1 = String::from("hello");
let s2 = s1.clone(); // explicit deep copy — both are validClosure
A closure is an anonymous function that can capture variables from its enclosing
scope. Rust closures implement one or more of the Fn, FnMut, and FnOnce
traits depending on how they use captured variables. They are written with pipe
delimiters: |params| body. Closures are the backbone of Rust's iterator API
and are used extensively for callbacks and higher-order functions.
let offset = 10;
let add_offset = |x| x + offset;
println!("{}", add_offset(5)); // 15Copy
Copy is a marker trait for types whose values are duplicated bit-for-bit
automatically when assigned or passed to a function, rather than moved. All
primitive types (i32, f64, bool, char) and tuples/arrays of Copy types
implement Copy. A type cannot implement Copy if it owns heap-allocated
memory, because that would create two owners of the same data.
Crate
A crate is the smallest compilation unit in Rust. There are two kinds: a
binary crate (produces an executable, has a main function) and a
library crate (produces a .rlib that other crates can depend on). Each
crate has a crate root — src/main.rs for binaries and src/lib.rs for
libraries. A package can contain multiple crates.
Deref Coercion
Deref coercion is a convenience feature that automatically converts a reference
to one type into a reference to another when the Deref trait is implemented.
For example, &String automatically coerces to &str, and &Vec<T> coerces to
&[T]. This means you can pass a &String to a function that expects &str
without explicit conversion.
fn greet(s: &str) { println!("Hello, {}!", s); }
let name = String::from("Alice");
greet(&name); // &String coerced to &str automaticallyDrop
The Drop trait lets you specify code to run when a value goes out of scope.
Rust calls drop() automatically — you never need to free memory manually.
Types like String, Vec, Box, and Mutex all implement Drop to release
their resources. You can implement Drop on your own types to clean up files,
network connections, or other resources deterministically.
DST (Dynamically Sized Type)
A dynamically sized type (DST) is a type whose size is not known at compile time.
The two most common DSTs are str (a string slice without a length baked in) and
trait objects (dyn Trait). Because the compiler cannot put a DST directly on
the stack, you always use them behind a reference (&str, &dyn Trait) or behind
a pointer (Box<dyn Trait>).
Enum
An enum (enumeration) is a type that can be exactly one of several named variants.
Rust enums are much more powerful than in most languages: each variant can hold
different data, making them algebraic data types. The standard library's Option<T>
and Result<T, E> are both enums. Enums are typically consumed with match or
if let.
enum Shape {
Circle(f64),
Rectangle(f64, f64),
Triangle { base: f64, height: f64 },
}
let area = match Shape::Circle(3.0) {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
};Fearless Concurrency
"Fearless concurrency" is Rust's promise that its ownership and type system
eliminate entire classes of concurrency bugs — data races, use-after-free across
threads, and unsynchronised mutation — at compile time. The Send and Sync
marker traits encode thread-safety requirements, and the compiler refuses code
that would violate them. You can write concurrent code without fear of the most
common bugs.
FFI (Foreign Function Interface)
The FFI allows Rust code to call functions written in C (and other languages that
expose a C ABI) and to expose Rust functions to C. You declare external functions
inside an extern "C" block. All FFI calls are inherently unsafe because Rust
cannot verify the safety guarantees of foreign code.
extern "C" {
fn abs(x: i32) -> i32;
}
fn main() {
unsafe { println!("{}", abs(-5)); }
}Future
A Future is a value that represents an asynchronous computation that will
produce a result at some point. Futures are lazy — they do no work until polled
by an async runtime (such as Tokio). The async fn syntax sugar desugars into a
function returning impl Future. You drive a future to completion by .await-ing
it inside another async context.
Generic
Generics allow you to write functions, structs, enums, and impl blocks that are
parameterised over one or more types. The compiler performs monomorphization —
generating a concrete copy for each distinct type used — so generics have zero
runtime overhead. Trait bounds constrain which types are acceptable, e.g.
fn largest<T: PartialOrd>(list: &[T]) -> &T.
impl Trait
impl Trait is a concise syntax for expressing a generic parameter or return
type that implements a given trait, without naming the concrete type. In parameter
position (fn f(x: impl Display)) it is equivalent to a generic bound. In return
position (fn f() -> impl Iterator) it tells callers the concrete type
implements the trait without revealing what it actually is.
Interior Mutability
Interior mutability is a design pattern that lets you mutate data through a
shared (immutable) reference, bypassing the usual borrow rules. Rust provides
Cell<T> and RefCell<T> for single-threaded interior mutability, and
Mutex<T> / RwLock<T> for multi-threaded use. RefCell<T> enforces the
borrow rules at runtime rather than compile time; a panic occurs if you violate
them.
use std::cell::RefCell;
let data = RefCell::new(vec![1, 2, 3]);
data.borrow_mut().push(4); // runtime borrow check
println!("{:?}", data.borrow());Iterator
The Iterator trait defines a sequence of values produced one at a time via the
next() -> Option<Self::Item> method. All collections in Rust expose iterators,
and the trait provides dozens of default adaptor methods (.map(), .filter(),
.fold(), etc.) that compose lazily. Iterators have zero overhead compared to
hand-written loops because they are inlined by the compiler.
Lifetime
A lifetime is a compile-time label that describes how long a reference is valid.
The borrow checker uses lifetimes to prove that no reference outlives the data it
points to. In most code lifetimes are inferred (elided). You write them explicitly
('a) only when the compiler cannot infer the relationship — most commonly in
function signatures with multiple reference parameters or in structs that hold
references.
// The returned reference lives at least as long as both inputs
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}Match
match is Rust's pattern-matching expression. It compares a value against a
series of patterns and executes the arm whose pattern matches. The compiler
enforces exhaustiveness — every possible value must be covered, either explicitly
or with a wildcard. match can destructure enums, structs, tuples, and ranges,
and can include match guards (if conditions on arms).
Module
Modules organise code into named namespaces and control visibility. You declare
one with mod name { ... } or by creating a file name.rs (or
name/mod.rs). Items inside a module are private by default; the pub keyword
makes them accessible to code outside. Use use statements to bring paths into
scope for shorter names.
Move Semantics
When you assign a value to a new variable or pass it to a function, Rust
moves ownership to the destination. The original binding becomes
invalid and the compiler will reject any subsequent use of it. Move semantics
eliminate the need for a garbage collector by making ownership transfers explicit
and statically verified. Types that implement Copy are an exception — they are
copied instead of moved.
Mutex
A Mutex<T> (mutual exclusion lock) wraps a value and ensures that only one
thread can access it at a time. You call .lock() to acquire the guard, which
dereferences to &mut T, and the lock is automatically released when the guard
is dropped. In Rust, Mutex is almost always used inside Arc so multiple
threads can hold a reference to the same mutex.
Newtype Pattern
The newtype pattern wraps an existing type in a single-field tuple struct to
create a distinct type with its own identity. This enforces type safety (a
Metres cannot accidentally be added to a Seconds), enables implementing
foreign traits on foreign types (working around the orphan rule), and can hide
implementation details from callers.
struct Metres(f64); struct Seconds(f64); // fn foo(m: Metres, s: Seconds) — compiler prevents mixing them up
Option
Option<T> is Rust's replacement for null pointers. It is an enum with two
variants: Some(T) when a value is present and None when it is absent. Because
the compiler forces you to handle both cases, null-pointer dereferences are
impossible. Rich combinator methods (.map(), .unwrap_or(), .and_then(), etc.)
make working with Option ergonomic without verbose match expressions.
Orphan Rule
The orphan rule (also called the coherence rule) states that you can implement a trait for a type only if either the trait or the type is defined in your crate. This prevents two crates from providing conflicting implementations of the same trait for the same type. The newtype pattern is the standard workaround when you need to implement a foreign trait for a foreign type.
Ownership
Ownership is Rust's core memory management model. Every value has exactly one owner; when the owner goes out of scope the value is automatically dropped (freed). Ownership can be transferred (moved) but not shared without borrowing. These three rules — single owner, scope-based drop, explicit transfer — enable memory safety without a garbage collector and are enforced entirely at compile time.
Package
A package is one or more crates grouped together by a Cargo.toml file. It must
contain at most one library crate (src/lib.rs) but may contain multiple binary
crates (in src/bin/). The Cargo.toml defines the package name, version,
edition, and all dependencies. Packages are the unit of publication to
crates.io.
Panic
A panic is an unrecoverable error that causes the current thread to unwind (or
abort, depending on configuration) and print a backtrace. Panics are triggered by
panic!("message"), out-of-bounds indexing, .unwrap() on None or Err, and
integer overflow in debug builds. For recoverable errors, return Result instead.
Panics can be caught across thread boundaries with std::panic::catch_unwind.
Pattern Matching
Pattern matching is the ability to test a value against a structure and
simultaneously bind its parts to variables. Rust supports it in match
expressions, if let, while let, let else, and even plain let bindings.
Patterns can match literals, ranges, enum variants, struct fields, tuple
elements, and can be combined with guards. The compiler ensures match arms are
exhaustive.
Rc
Rc<T> (Reference Counted) is a single-threaded smart pointer for shared
ownership. Multiple Rc clones can point to the same heap allocation; the data
is dropped only when the last clone is dropped. Because it is not thread-safe, the
compiler prevents sending an Rc across a thread boundary. For multi-threaded
shared ownership, use Arc<T>.
Reference
A reference is a non-owning pointer to a value, written &T (shared/immutable)
or &mut T (exclusive/mutable). References are guaranteed by the borrow checker
to always point to valid data. You can have many &T references simultaneously,
or exactly one &mut T reference — but not both at the same time. References
implement Deref, so they coerce to inner types automatically.
Result
Result<T, E> is an enum for recoverable errors with two variants: Ok(T) on
success and Err(E) on failure. Functions that can fail should return Result
rather than panicking. The ? operator propagates errors automatically, and a rich
combinator API (.map(), .and_then(), .unwrap_or_else(), etc.) makes
composition ergonomic.
Send
Send is a marker trait that indicates a type is safe to transfer ownership to
another thread. The compiler automatically implements Send for most types. Raw
pointers and Rc<T> are the main exceptions because they are not thread-safe. If
a type contains any !Send field, it is automatically !Send too. The compiler
uses this to prevent you from accidentally sending unsafe values across threads.
Slice
A slice is a dynamically-sized view into a contiguous sequence of elements in
memory. Written &[T] for element slices and &str for string slices. Slices
carry both a pointer and a length, so they know their own size without owning the
data. They are obtained from arrays (&arr[1..3]), Vecs (&v[..]), or
Strings (&s[0..5]).
Smart Pointer
A smart pointer is a data structure that acts like a pointer but provides
additional capabilities such as automatic memory management or extra metadata.
Rust's smart pointers implement the Deref and Drop traits. The most common
ones are Box<T> (heap allocation), Rc<T> / Arc<T> (shared ownership), and
RefCell<T> / Mutex<T> (interior mutability).
Struct
A struct is a custom data type that groups named fields together. Rust has three
kinds: named structs (struct Point { x: f64, y: f64 }), tuple structs
(struct Color(u8, u8, u8)), and unit structs (struct Marker). Methods and
associated functions are defined in impl blocks. Structs can derive common
traits like Debug, Clone, and PartialEq.
Sync
Sync is a marker trait that indicates it is safe to share a reference to a
type across threads (i.e. &T can be sent to another thread). A type is Sync
if and only if &T is Send. Types with interior mutability that is not
thread-safe, like RefCell<T> and Cell<T>, are !Sync. Mutex<T> is Sync
because it protects its data.
Trait
A trait defines a shared interface — a set of method signatures that types can
implement. Traits are similar to interfaces in other languages but more powerful:
they support default implementations, associated types, and blanket implementations
(impl<T: Foo> Bar for T). The compiler uses trait bounds to verify that a
generic type supports the operations you need.
Trait Bound
A trait bound constrains a generic type parameter to only accept types that
implement a given trait. Written as T: Trait in angle brackets or after a
where keyword. Multiple bounds are combined with +: T: Clone + Debug.
Trait bounds are checked by the compiler at every call site, providing both
safety and zero-cost monomorphized code.
Trait Object
A trait object (dyn Trait) is a fat pointer to a heap- or stack-allocated value
plus a vtable that holds pointers to the value's trait method implementations.
Unlike generics (monomorphized at compile time), trait objects enable runtime
polymorphism — the concrete type is not known until runtime. They are used as
&dyn Trait or Box<dyn Trait> because they are DSTs.
Unit Type
The unit type () is a zero-sized type with exactly one value: the empty tuple
(). Functions that do not return a meaningful value implicitly return ().
Result<(), E> is the canonical type for fallible operations that succeed with
no output. Unit structs are a closely related concept — they too are zero-sized.
Unsafe
The unsafe keyword marks a block or function as opting out of some of Rust's
compile-time safety guarantees. Inside an unsafe block you can dereference raw
pointers, call unsafe functions, access mutable statics, implement unsafe
traits, and access union fields. Unsafe code is not inherently wrong, but the
programmer takes responsibility for upholding the invariants the compiler can no
longer check.
Vec
Vec<T> is the standard growable, heap-allocated array. It stores elements
contiguously in memory and resizes automatically when capacity is exceeded. The
three key fields are a pointer to the heap buffer, a length (number of elements),
and a capacity (allocated slots). Vec implements Deref<Target = [T]> so
slice methods work on it directly.
let mut v: Vec<i32> = Vec::new(); v.push(1); v.push(2); v.push(3); let third = v[2]; let slice: &[i32] = &v[1..];
Zero-Cost Abstraction
A zero-cost abstraction is a high-level language feature that compiles to the same machine code you would write by hand in C — it adds no runtime overhead. Rust's iterators, generics (via monomorphization), closures, and async/await are all zero-cost abstractions. The principle is: "What you don't use, you don't pay for. What you do use, you couldn't hand-code any better."