# Rust Core Stdlib Overview

> Use when the user needs a map of the Rust standard library: which std module provides what (collections, sync, io, fs, process, thread, time, path, env, ffi), the prelude, the core / alloc / std split, no_std switch, hash-DoS resilience. Prevents reaching for an external crate when std already has it, missing the no_std implications of using std::collections, or ignoring HashMap's RandomState hash-DoS protection. Covers: std::collections (Vec / HashMap / BTreeMap / VecDeque / HashSet / BTreeSet / BinaryHeap), std::sync (Arc / Mutex / RwLock / atomic / Once / OnceLock 1.70 / LazyLock 1.80 / Barrier / Condvar), std::io (Read / Write / BufReader / stdin / stdout / stderr), std::fs, std::process (Command), std::thread (spawn / scope 1.63+), std::time, std::path, std::env, std::ffi (CString / OsString), prelude, core / alloc / std split, hashbrown. Keywords: stdlib, standard library, prelude, no_std, alloc, core, collections, HashMap, Vec, BTreeMap, Arc, Mutex, RwLock, atomic, LazyLock, OnceLock, Read trait, Write

- Skill: `impertio-studio/rust-core-stdlib-overview` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add impertio-studio/rust-core-stdlib-overview`
- Raw SKILL.md: https://api.skillmd.com/api/skills/impertio-studio/rust-core-stdlib-overview/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: Impertio-Studio (https://skillmd.com/u/impertio-studio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/impertio-studio/rust-core-stdlib-overview

---


# rust-core-stdlib-overview

A tour of the Rust standard library. This skill is a **map**, not a tutorial: it tells you which `std` module provides which capability, what the `prelude` auto-imports, how `core` / `alloc` / `std` are layered, and when to drop to `no_std`. Deep mechanics live in cross-referenced skills.

Cross-references: [[rust-impl-no-std]] [[rust-impl-concurrency]] [[rust-impl-channels]] [[rust-core-async-runtime]] [[rust-impl-async-tokio]]

---

## When to use this skill

- User asks "is there a standard library type for X" or "do I need a crate for this"
- User asks "what does the prelude import", "what is `std::prelude::v1`"
- User asks "what is the difference between `core`, `alloc`, and `std`"
- User asks "how do I make my crate `no_std`" at the map level (mechanics live in `rust-impl-no-std`)
- User confused about which `std::sync` primitive to pick at the catalogue level
- User asks "which `std::collections` type should I use" at the catalogue level
- User asks "where is X in `std`" and you need the module location

For deep mechanics of any module (lock poisoning, async runtimes, lifetime rules of `&Path`), refer to the cross-referenced skills.

---

## Layering: core, alloc, std

`std` is built on two lower layers. ALWAYS understand which layer you depend on before writing portable code.

| Layer | What it provides | Requires |
|-------|------------------|----------|
| `core` | Primitives, traits, `Option`, `Result`, `Iterator`, formatting, atomics, `Future`, `Pin`. No allocation, no OS. | Always available, including bare-metal `no_std`. |
| `alloc` | `Box`, `Vec`, `String`, `Rc`, `Arc`, `BTreeMap`, `BTreeSet`, `VecDeque`, `BinaryHeap`, `LinkedList`. | A global allocator. Available on `no_std` with `extern crate alloc;`. |
| `std` | Everything in `core` and `alloc` plus OS-dependent: `std::fs`, `std::io`, `std::net`, `std::process`, `std::thread`, `std::env`, `std::sync::Mutex`, `HashMap`, `HashSet`. | An operating system. |

ALWAYS note: `HashMap` and `HashSet` are in `std`, NOT in `alloc`, because they depend on `RandomState` for hash-DoS resilience which is seeded from the OS RNG. NEVER assume you can use `HashMap` on `no_std`; use the `hashbrown` crate directly with a manual hasher.

ALWAYS note: `std::collections` re-exports the `alloc::collections` types (`BTreeMap`, `BTreeSet`, `VecDeque`, `BinaryHeap`, `LinkedList`) plus the `std`-only types (`HashMap`, `HashSet`). Switching to `no_std + alloc` loses the hash-based types.

---

## The prelude

The **prelude** is the set of names automatically imported into every module of every crate. Edition 2024 adds two items.

| Item | Prelude version |
|------|-----------------|
| `Copy`, `Clone`, `Drop`, `Sized`, `Send`, `Sync`, `Unpin` | v1 (all editions) |
| `Option`, `Some`, `None`, `Result`, `Ok`, `Err` | v1 (all editions) |
| `Box`, `String`, `ToString`, `Vec`, `ToOwned` | v1 (all editions) |
| `Default`, `From`, `Into`, `TryFrom`, `TryInto` | v1 (all editions) |
| `Iterator`, `IntoIterator`, `DoubleEndedIterator`, `ExactSizeIterator`, `Extend` | v1 (all editions) |
| `AsRef`, `AsMut` | v1 (all editions) |
| `Debug`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd` | v1 (all editions) |
| `Fn`, `FnMut`, `FnOnce` | v1 (all editions) |
| `Future`, `IntoFuture` | edition 2024 prelude addition |

ALWAYS access the prelude as `std::prelude::v1` (or `core::prelude::v1` / `alloc::prelude::v1` for the layered preludes). NEVER assume non-prelude items like `std::collections::HashMap` are auto-imported; you must `use` them.

---

## Decision table: I need X to do Y, which std module

| I need to ... | Use this `std` module | Key type or function |
|---------------|------------------------|----------------------|
| Store a growable list | `std::vec` | `Vec<T>` |
| Look up by key (unordered) | `std::collections` | `HashMap<K, V>` |
| Look up by key (sorted) | `std::collections` | `BTreeMap<K, V>` |
| Queue / double-ended queue | `std::collections` | `VecDeque<T>` |
| Set of unique values (unordered) | `std::collections` | `HashSet<T>` |
| Set of unique values (sorted) | `std::collections` | `BTreeSet<T>` |
| Max-heap priority queue | `std::collections` | `BinaryHeap<T>` |
| Share data across threads | `std::sync` | `Arc<T>` |
| Mutual exclusion lock | `std::sync` | `Mutex<T>` |
| Multi-reader / single-writer lock | `std::sync` | `RwLock<T>` |
| Lock-free counter / flag | `std::sync::atomic` | `AtomicUsize`, `AtomicBool`, `Ordering` |
| One-time initialization (thread-safe, returns reference) | `std::sync` | `OnceLock<T>` (since 1.70) |
| Lazy global (thread-safe, holds closure) | `std::sync` | `LazyLock<T, F>` (since 1.80) |
| Wait for N threads | `std::sync` | `Barrier` |
| Condition variable | `std::sync` | `Condvar` |
| Read or write a file | `std::fs` + `std::io` | `File::open`, `File::create`, `fs::read_to_string`, `fs::write` |
| Buffered I/O | `std::io` | `BufReader<R>`, `BufWriter<W>` |
| Read stdin / write stdout | `std::io` | `io::stdin`, `io::stdout`, `io::stderr` |
| Spawn an OS thread | `std::thread` | `thread::spawn`, `thread::scope` (since 1.63) |
| Run a subprocess | `std::process` | `Command::new(...).output()` |
| Sleep / measure elapsed time | `std::time` + `std::thread` | `thread::sleep`, `Instant::now`, `Duration::from_*` |
| Wall-clock time | `std::time` | `SystemTime::now` |
| Manipulate filesystem paths | `std::path` | `Path`, `PathBuf` |
| Read environment variables / args | `std::env` | `env::args`, `env::var`, `env::vars`, `env::current_dir` |
| FFI string with C (NUL-terminated) | `std::ffi` | `CString`, `CStr` |
| FFI string with OS (platform-native) | `std::ffi` | `OsString`, `OsStr` |
| Format a string | `std::fmt` | `format!`, `write!`, `Debug`, `Display` |
| Pin a value | `std::pin` | `Pin<P>`, `pin!` macro |
| Channel between threads | `std::sync::mpsc` | `channel`, `Sender`, `Receiver` |

ALWAYS check this table before reaching for an external crate. NEVER add a dependency for capabilities that `std` already provides at acceptable quality.

---

## std::collections at a glance

`std::collections` is the catalogue of generic containers. Selection rules:

- ALWAYS default to `Vec<T>` for sequences. It is the most efficient choice for almost every use case.
- ALWAYS prefer `HashMap<K, V>` for unordered key-value lookup when keys implement `Hash + Eq`.
- ALWAYS use `BTreeMap<K, V>` when you need ordered iteration, range queries, or deterministic iteration order (HashMap iteration order is intentionally randomised).
- ALWAYS use `VecDeque<T>` for FIFO queues; `Vec::remove(0)` is O(n), `VecDeque::pop_front` is O(1).
- ALWAYS use `BinaryHeap<T>` for priority queues; it is a max-heap, use `std::cmp::Reverse` for a min-heap.
- NEVER reach for `LinkedList<T>`; its only honest use case is constant-time splicing of large lists, which is almost never the bottleneck. `Vec` or `VecDeque` is faster in practice due to cache locality.

### HashMap and hash-DoS resilience

`HashMap` uses `RandomState` as its default hasher. `RandomState` is seeded from the OS RNG at creation time. This is a deliberate hash-DoS protection: an attacker who can choose keys cannot force pathological O(n) collisions in the table.

Consequences:

- ALWAYS expect iteration order to differ between runs and between insertions. NEVER rely on `HashMap` iteration order for any logic.
- For deterministic order, use `BTreeMap` or wrap with `IndexMap` (external crate `indexmap`).
- For maximum speed when keys are trusted (internal benchmark, fixed dataset), use `HashMap` with a non-random hasher such as `FxHashMap` (external crate `rustc-hash`) or `AHashMap` (external crate `ahash`). ALWAYS document the threat model when removing DoS protection.
- The underlying implementation of `HashMap` is the `hashbrown` crate, which is the Swiss-table design used by ABSL. In `no_std + alloc` you depend on `hashbrown` directly.

---

## std::sync at a glance

`std::sync` collects the synchronization primitives.

- `Arc<T>` : atomic reference count, thread-safe shared ownership. ALWAYS use `Arc`, NEVER `Rc`, when sharing across threads.
- `Mutex<T>` : exclusive lock. Returns `LockResult<MutexGuard<T>>`; `Err` indicates **poisoning** from a panic while locked. ALWAYS handle poisoning explicitly or call `.unwrap()` only when the panic is unrecoverable.
- `RwLock<T>` : many readers OR one writer. ALWAYS prefer `Mutex` for write-heavy workloads; `RwLock` overhead beats `Mutex` only when reads dominate.
- `atomic` (`AtomicUsize`, `AtomicBool`, `AtomicPtr`, `AtomicI32`, etc.) : lock-free integer / pointer operations with explicit `Ordering`. ALWAYS pick `Ordering::Relaxed` for counters with no cross-thread observation requirements; `Ordering::Acquire` / `Ordering::Release` for synchronization; `Ordering::SeqCst` only when you have proven a need.
- `Once` : run a closure exactly once across threads. Legacy; ALWAYS prefer `OnceLock` or `LazyLock` for new code.
- `OnceLock<T>` (since 1.70) : one-time initialization that returns `&T`. Replaces `lazy_static` for non-closure cases.
- `LazyLock<T, F>` (since 1.80) : `static` lazily initialized by a closure on first access. Replaces `lazy_static` for closure cases.
- `Barrier` : block N threads until all arrive.
- `Condvar` : condition variable, used with `Mutex<bool>` or `Mutex<Queue>` for "wait until predicate holds" patterns.
- `mpsc` (sub-module): multi-producer, single-consumer channel. `channel()` returns `(Sender<T>, Receiver<T>)`; `sync_channel(bound)` for bounded backpressure.

NEVER use `std::sync::Mutex` inside `async` code (holding the guard across `.await` blocks the runtime worker). Use `tokio::sync::Mutex` or similar runtime-specific async locks. See [[rust-core-async-runtime]].

---

## std::io at a glance

`std::io` defines the I/O traits and helpers. Almost all `Read` / `Write` consumers are generic over the trait, not concrete types.

| Item | Purpose |
|------|---------|
| `Read` | `read(&mut self, buf: &mut [u8]) -> io::Result<usize>` |
| `Write` | `write(&mut self, buf: &[u8]) -> io::Result<usize>`, plus `write_all`, `flush` |
| `BufRead` | `read_line`, `lines()`, `fill_buf` |
| `Seek` | `seek(SeekFrom)` |
| `BufReader<R>` | Wraps a `Read` with an in-memory buffer; ALWAYS wrap raw `File` reads in `BufReader` unless you are doing one big read |
| `BufWriter<W>` | Wraps a `Write` with an in-memory buffer; flush on drop (errors are swallowed; flush explicitly to detect them) |
| `io::stdin()`, `io::stdout()`, `io::stderr()` | Handles to the standard streams; each acquires a lock per call (prefer `.lock()` for tight loops) |
| `io::Result<T>` | Alias for `Result<T, io::Error>` |
| `io::Error`, `io::ErrorKind` | Error type and discriminant; use `ErrorKind::NotFound`, `WouldBlock`, etc. for matching |
| `io::copy(&mut R, &mut W)` | Copy bytes between any `Read` and any `Write` |

ALWAYS check the return of `Write::write`: a partial write is legal. Use `write_all` to loop until done, or handle the partial yourself.

NEVER call `stdout().write_all(...)` in a tight loop without locking once via `stdout().lock()`; each `stdout()` call acquires the lock and is significantly slower.

---

## std::fs and std::path at a glance

`std::fs` covers filesystem operations; `std::path` covers cross-platform path manipulation.

Key items:

- `File::open(path)` opens read-only; `File::create(path)` opens write-only-truncate; `OpenOptions::new().read(true).write(true).create(true).append(true).open(path)` for full control.
- `fs::read_to_string(path)` reads an entire file into a `String`; `fs::write(path, contents)` writes a slice or string.
- `fs::metadata(path)` returns `Metadata` (file size, mtime, permissions).
- `fs::create_dir`, `fs::create_dir_all`, `fs::remove_file`, `fs::remove_dir`, `fs::remove_dir_all`, `fs::rename`, `fs::copy`.
- `fs::read_dir(path)` returns an iterator of `io::Result<DirEntry>`.

`Path` is the **borrowed** unsized type (analogous to `&str`); `PathBuf` is the **owned** growable type (analogous to `String`).

- ALWAYS accept `&Path` (or generically `impl AsRef<Path>`) in function parameters, NEVER `&PathBuf`.
- ALWAYS use `Path::join`, `Path::with_extension`, `Path::file_name`, `Path::components` for manipulation; NEVER concatenate strings.
- NEVER assume paths are valid UTF-8; on Unix they are arbitrary bytes, on Windows they are arbitrary UTF-16. Use `Path::display()` for human output and `OsStr` for byte-level work.

NEVER use blocking `std::fs` calls inside an async runtime (Tokio, async-std, smol); the runtime worker stalls. Use `tokio::fs` or wrap in `tokio::task::spawn_blocking`. See [[rust-impl-async-tokio]].

---

## std::process at a glance

`std::process::Command` is the builder for spawning subprocesses.

```rust
use std::process::{Command, Stdio};

let output = Command::new("ls")
    .arg("-la")
    .arg("/tmp")
    .env("LC_ALL", "C")
    .stdout(Stdio::piped())
    .output()?; // io::Result<Output>

assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
```

Key API:

- `Command::new(program)` ; chained `.arg`, `.args`, `.env`, `.env_clear`, `.env_remove`, `.current_dir`, `.stdin`, `.stdout`, `.stderr`.
- `.output()` runs to completion and collects `Output { status, stdout, stderr }`.
- `.status()` runs to completion and returns only the exit status.
- `.spawn()` returns `Child` for streaming I/O; ALWAYS call `.wait()` or `.wait_with_output()` to reap the process.
- `process::exit(code)` terminates the current process without unwinding (Drop is NOT run).
- `process::abort()` aborts (no destructors, signal SIGABRT on Unix).

NEVER pass user-controlled strings through a shell. ALWAYS use `Command::arg` per argument; arguments are passed directly to the OS without shell interpretation.

---

## std::thread at a glance

`std::thread` is the OS-thread API.

- `thread::spawn(|| { ... })` returns a `JoinHandle<T>`; closure must be `'static + Send`, return type must be `Send`.
- `thread::scope(|s| { s.spawn(|| { ... }); ... })` (since 1.63) creates **scoped threads** that can borrow non-`'static` data; all scoped threads must finish before the scope returns. ALWAYS prefer `thread::scope` over `thread::spawn` when you need to borrow stack data.
- `JoinHandle::join()` waits for the thread and returns `Result<T, Box<dyn Any + Send>>`; `Err` indicates the thread panicked.
- `thread::Builder::new().name("worker").stack_size(8 * 1024 * 1024).spawn(...)` configures a thread before spawning.
- `thread::current()` returns the current thread handle; `thread::sleep(Duration)`; `thread::park()` / `Thread::unpark()`.
- `thread_local!` macro declares per-thread static storage.

ALWAYS join spawned threads, NEVER let them detach silently unless you genuinely have a fire-and-forget background task.

---

## std::time at a glance

| Type | Purpose |
|------|---------|
| `Duration` | Span of time; constructors `Duration::from_secs`, `from_millis`, `from_micros`, `from_nanos`. Arithmetic via `+`, `-`, `*`, `/`. |
| `Instant` | Monotonic clock; `Instant::now()`, `Instant::elapsed`, `Instant::duration_since`. ALWAYS use for measuring elapsed time. |
| `SystemTime` | Wall-clock time; `SystemTime::now()`, `SystemTime::UNIX_EPOCH`. NEVER use for measuring elapsed time; the wall clock can jump backwards. |

`thread::sleep(Duration::from_millis(100))` sleeps the current thread.

For async sleep, use `tokio::time::sleep` ; NEVER use `thread::sleep` inside an async runtime.

---

## std::env at a glance

| Function | Returns |
|----------|---------|
| `env::args()` | `Args` iterator over `String` command-line arguments (panics on non-UTF-8 on Unix; use `env::args_os()` for `OsString`) |
| `env::var("NAME")` | `Result<String, VarError>`; UTF-8 only |
| `env::var_os("NAME")` | `Option<OsString>`; any bytes |
| `env::vars()` | Iterator over `(String, String)` pairs (UTF-8 only) |
| `env::current_dir()` | `io::Result<PathBuf>` |
| `env::set_current_dir(path)` | `io::Result<()>` ; process-wide, NOT thread-local |
| `env::current_exe()` | `io::Result<PathBuf>` ; path of the running binary |
| `env::set_var(k, v)` / `env::remove_var(k)` | Modifies the process environment (since 1.85: unsafe-marked; calling concurrently from multiple threads is undefined behavior) |

ALWAYS use `env::args_os()` when arguments may contain non-UTF-8 (filenames on Unix). NEVER mutate environment variables (`set_var` / `remove_var`) from inside a library or from a multi-threaded program without external synchronization; libc `setenv` is not thread-safe.

---

## std::ffi at a glance

Two pairs of borrowed/owned string types, with different invariants.

| Type | Purpose |
|------|---------|
| `CStr` | Borrowed NUL-terminated C string. Unsized, like `str`. |
| `CString` | Owned NUL-terminated C string. Constructed via `CString::new(...)`. |
| `OsStr` | Borrowed platform-native string. Unsized. |
| `OsString` | Owned platform-native string. |

ALWAYS use `CString` / `CStr` for FFI with C APIs (`extern "C"` functions that take `*const c_char`). NEVER pass `&str` directly; `&str` is not NUL-terminated.

ALWAYS use `OsString` / `OsStr` for paths and environment variables that may contain non-UTF-8 bytes (Unix paths, Windows UTF-16 paths).

Conversion: `CString::new("hello")?` returns `Result<CString, NulError>`; the error fires if the input contains an interior NUL byte.

See [[rust-impl-ffi-bindgen]] for full FFI mechanics.

---

## The no_std switch

To make a crate compile without `std`, add `#![no_std]` to `src/lib.rs`. Consequences:

- You lose `std::fs`, `std::io`, `std::net`, `std::process`, `std::thread`, `std::env`, `std::sync::Mutex`, `HashMap`, `HashSet`, the standard `println!` macro that targets stdout.
- You keep all of `core` : `Option`, `Result`, `Iterator`, traits, atomics, formatting machinery (but no default output target).
- ALWAYS add `extern crate alloc;` if you want `Box`, `Vec`, `String`, `Rc`, `Arc`, `BTreeMap`. Then `use alloc::vec::Vec;`, etc.
- ALWAYS provide a `#[panic_handler]` somewhere in the crate graph (typically in the binary or board crate, not in the library).
- For hash maps without `std`, use the `hashbrown` crate directly with `BuildHasherDefault<FxHasher>` or a fixed seed.

See [[rust-impl-no-std]] for the full `no_std` workflow.

---

## Common cross-references

- For ownership and borrowing rules used throughout the stdlib API: see [[rust-syntax-ownership]] and [[rust-syntax-borrowing]].
- For deeper async runtime semantics (when `std` blocking calls are forbidden): see [[rust-core-async-runtime]] and [[rust-impl-async-tokio]].
- For lock-free programming with `std::sync::atomic`: see [[rust-impl-concurrency]].
- For channel patterns built on `std::sync::mpsc`: see [[rust-impl-channels]].
- For the `no_std` workflow end to end: see [[rust-impl-no-std]].

---

## Reference files

- `references/methods.md` : exact module item names per area, grouped by `std` module
- `references/examples.md` : minimal working example for each major area
- `references/anti-patterns.md` : five-plus stdlib anti-patterns and their fixes

---

## Approved sources

- Rust Standard Library (root): https://doc.rust-lang.org/std/
- std::collections : https://doc.rust-lang.org/std/collections/index.html
- std::sync : https://doc.rust-lang.org/std/sync/index.html
- std::io : https://doc.rust-lang.org/std/io/index.html
- std::fs : https://doc.rust-lang.org/std/fs/index.html
- std::thread : https://doc.rust-lang.org/std/thread/index.html
- std::time : https://doc.rust-lang.org/std/time/index.html
- std::path : https://doc.rust-lang.org/std/path/index.html
- std::env : https://doc.rust-lang.org/std/env/index.html
- std::ffi : https://doc.rust-lang.org/std/ffi/index.html
- std::process : https://doc.rust-lang.org/std/process/index.html
- Rust 1.70 release (OnceLock) : https://blog.rust-lang.org/2023/06/01/Rust-1.70.0/
- Rust 1.80 release (LazyLock) : https://blog.rust-lang.org/2024/07/25/Rust-1.80.0/
- Rust 1.63 release (scoped threads) : https://blog.rust-lang.org/2022/08/11/Rust-1.63.0/

