Rust essentials
The Rust 1.98 syntax, error patterns, concurrency tools, and Cargo commands worth keeping beside the editor.
Rust 1.98 1 page when printed
Bindings and control flow
let value = source; bind an immutable local; shadow it with another let when the type must change let mut count = 0; permit reassignment and in-place mutation const LIMIT: usize = 100; declare a typed compile-time constant let Some(id) = maybe_id else { return; }; extract a matching value or leave the current control flow match result { Ok(value) => value, Err(error) => return Err(error) } handle every variant and return the original error while let Some(item) = stack.pop() { consume(item); } keep consuming values until the pattern stops matching Ownership and borrowing
inspect(&value); lend shared access without transferring ownership update(&mut value); lend exclusive mutable access until the borrow’s last use let moved = value; transfer ownership and invalidate the old binding unless the type is Copy let copied = number; copy a Copy value so both bindings remain usable let cloned = value.clone(); request an explicit duplicate through Clone let old = std::mem::take(&mut value); replace with Default::default() and take the old value drop(guard); release an owned resource before the end of its scope Strings and collections
fn normalize(text: &str) -> String { text.trim().to_owned() } borrow UTF-8 input and return owned text text.chars() iterate Unicode scalar values, not grapheme clusters text.as_bytes() borrow the underlying UTF-8 bytes without allocating String::from_utf8(bytes)? validate UTF-8 and reuse the owned byte buffer Vec::with_capacity(count) reserve space when the expected item count is known counts.entry(key).or_insert(0) insert a missing map value and borrow it mutably queue.pop_front() remove the oldest VecDeque item as an Option Iterators and closures
items.iter() yield shared references and keep the collection items.iter_mut() yield exclusive references for in-place updates items.into_iter() consume the collection and yield owned items iter.filter_map(parse).collect::<Vec<_>>() discard None values and collect successful projections iter.collect::<Result<Vec<_>, _>>()? stop at the first error while collecting successful items move |item| process(item, &config) capture config by value for a closure that may outlive this scope F: FnMut(&Item) -> bool accept a repeatable predicate that may mutate captured state Traits and types
#[derive(Debug, Clone, PartialEq)] struct Point(i32, i32); generate standard trait implementations for a data type struct UserId(u64); give a primitive a distinct domain type with the newtype pattern enum State<T> { Ready(T), Closed } model a closed set of states with associated data fn show<T: Display>(value: &T) { println!("{value}"); } require a capability with a generic trait bound trait Source { type Item; fn next(&mut self) -> Option<Self::Item>; } let each implementation choose one associated item type Box<dyn Draw + Send> own a type-erased implementor that can cross thread boundaries <User as Display>::fmt(&user, formatter) select a trait implementation explicitly with fully qualified syntax Option and Result
type LoadResult<T> = Result<T, LoadError>; name a reusable result type without hiding its error let value = operation()?; unwrap success or return a converted residual immediately option.ok_or_else(|| Error::Missing(key))? turn absence into a lazily constructed error option.and_then(parse) chain a function that returns another Option result.map(transform) transform only the success value result.map_err(Error::from) convert only the error value matches!(status, Status::Ready(_)) test a pattern and return a boolean anyhow and thiserror
type AppResult<T> = anyhow::Result<T>; erase concrete error types at an application boundary read(path).with_context(|| format!("reading {path}"))? after importing anyhow::Context, add context only on failure anyhow::bail!("invalid port: {port}"); return an ad hoc application error immediately anyhow::ensure!(port > 0, "port must be positive"); return an application error when an invariant is false #[derive(Debug, thiserror::Error)] enum LoadError { #[error("missing config")] Missing } define a concrete error that callers can match #[error("cannot read {path}: {source}")] Read { path: PathBuf, #[source] source: std::io::Error } format a variant while preserving its lower-level source Io(#[from] std::io::Error) generate From conversion and error-source reporting for a variant Threads and shared state
let handle = std::thread::spawn(move || work(data)); move owned task data into a new operating-system thread let result = handle.join().expect("worker panicked"); wait for a thread and surface a panic explicitly let shared = Arc::new(Mutex::new(value)); combine shared ownership with synchronized mutation let mut guard = shared.lock().unwrap(); acquire mutable access; handle poisoning when recovery matters let read_guard = lock.read().unwrap(); allow concurrent readers through an RwLock guard let (sender, receiver) = std::sync::mpsc::sync_channel(capacity); create a bounded channel whose full buffer applies backpressure sender.send(value)?; transfer a value to the receiver or report disconnection Async and Tokio
async fn fetch() -> Result<Data, Error> { request().await } return a future whose body runs when polled let value = future.await?; suspend until completion and propagate failure let handle = tokio::spawn(async move { work(data).await }); spawn an owned Send + 'static task on the runtime let (left, right) = tokio::try_join!(load_a(), load_b())?; drive fallible futures concurrently and stop on an error tokio::select! { value = task => value, } race enabled branches; losing futures must be cancellation-safe let value = tokio::time::timeout(duration, operation()).await??; bound the wait and propagate both timeout and operation errors tokio::task::spawn_blocking(|| blocking_work()).await?; move blocking work off asynchronous worker threads Modules and Cargo
mod parser; declare a module whose source the compiler locates by module rules use crate::parser::parse; bring an item into scope from the current crate root pub(crate) fn helper() {} expose an item only within the current crate pub use api::Client; re-export an item as part of the public API cargo check --all-targets --all-features type-check every target with every feature enabled cargo test --workspace run tests for every package in the workspace cargo clippy --all-targets --all-features -- -D warnings lint all targets and fail on any warning Unsafe and FFI
let ptr = &raw const value; create a raw pointer without creating an intermediate reference let ptr = NonNull::new(ptr).ok_or(Error::Null)?; reject null without claiming the pointer is otherwise valid let value = unsafe { ptr.read() }; read only after proving alignment, initialization, and validity let slice = unsafe { std::slice::from_raw_parts(ptr, len) }; build a slice only after proving the full memory-range contract #[repr(C)] struct Header { tag: u32, len: usize } request C-compatible field layout for an FFI data structure unsafe extern "C" { fn strlen(s: *const c_char) -> usize; } declare a foreign function whose call contract Rust cannot verify let text = unsafe { CStr::from_ptr(ptr) }; borrow a C string only after proving termination and pointer validity Say it precisely to your AI
rules pack · Rust
Rust rules for your coding agent
Download the track's pitfalls and review checks in the format your coding agent reads.