Skip to main content
The Rust Standard Library is the foundation of portable Rust software, a set of minimal and battle-tested shared abstractions for the broader Rust ecosystem. It offers core types, library-defined operations on language primitives, standard macros, I/O, multithreading, and much more.
std is available to all Rust crates by default. Access it in use statements through the path std, as in use std::env.

Overview

The standard library provides:
  • Core types like Vec<T>, Option<T>, Result<T, E>
  • Platform abstractions for OS interfaces
  • I/O operations for files, networking, and streams
  • Multithreading and concurrency primitives
  • Collections and data structures
  • String handling and text processing

Key Modules

collections

HashMap, HashSet, and other collections

io

Input/output operations

fs

Filesystem operations

net

TCP/UDP networking

thread

Threading abstractions

sync

Synchronization primitives

process

Process management

env

Environment interaction

path

Path manipulation

Containers and Collections

Optional and Error Types

fn divide(numerator: f64, denominator: f64) -> Option<f64> {
    if denominator == 0.0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

match divide(4.0, 2.0) {
    Some(result) => println!("Result: {}", result),
    None => println!("Cannot divide by zero"),
}
Common methods:
  • unwrap() - Get value or panic
  • expect(msg) - Unwrap with custom message
  • unwrap_or(default) - Get value or default
  • map(f) - Transform the contained value
  • and_then(f) - Chain operations
use std::fs::File;
use std::io::Read;

fn read_file(path: &str) -> Result<String, std::io::Error> {
    let mut file = File::open(path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

match read_file("data.txt") {
    Ok(contents) => println!("File contents: {}", contents),
    Err(e) => eprintln!("Error reading file: {}", e),
}
The ? operator: Propagates errors automatically in functions returning Result.

Collections

let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);

let v = vec![1, 2, 3];
let v = vec![0; 10]; // ten zeroes

// Iteration
for item in &v {
    println!("{}", item);
}

// Methods
v.extend([4, 5, 6]);
v.retain(|&x| x % 2 == 0);
v.sort();
use std::collections::HashMap;

let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Red"), 50);

// Access
let score = scores.get("Blue");
let score = scores.get("Blue").copied().unwrap_or(0);

// Iteration
for (key, value) in &scores {
    println!("{}: {}", key, value);
}

// Entry API
scores.entry(String::from("Yellow")).or_insert(50);
use std::collections::HashSet;

let mut set = HashSet::new();
set.insert(1);
set.insert(2);
set.insert(2); // Duplicate ignored

// Set operations
let a: HashSet<_> = [1, 2, 3].iter().cloned().collect();
let b: HashSet<_> = [2, 3, 4].iter().cloned().collect();

let union: HashSet<_> = a.union(&b).cloned().collect();
let intersection: HashSet<_> = a.intersection(&b).cloned().collect();

Platform Abstractions and I/O

File System Operations

use std::fs;
use std::io::prelude::*;

// Reading files
let contents = fs::read_to_string("file.txt")?;
let bytes = fs::read("file.bin")?;

// Writing files
fs::write("output.txt", "Hello, world!")?;

// File operations
let mut file = fs::File::create("log.txt")?;
file.write_all(b"Log entry\n")?;

// Directory operations
fs::create_dir("new_dir")?;
fs::create_dir_all("path/to/nested/dir")?;
fs::remove_dir("old_dir")?;

// Metadata
let metadata = fs::metadata("file.txt")?;
println!("File size: {} bytes", metadata.len());

// Reading directories
for entry in fs::read_dir(".")? {
    let entry = entry?;
    println!("Found: {:?}", entry.path());
}

I/O Operations

use std::io::{self, Read, Write, BufRead, BufReader};

// Reading from stdin
let mut input = String::new();
io::stdin().read_line(&mut input)?;

// Writing to stdout/stderr
println!("To stdout");
eprintln!("To stderr");

// Buffered I/O
let file = fs::File::open("data.txt")?;
let reader = BufReader::new(file);

for line in reader.lines() {
    let line = line?;
    println!("Line: {}", line);
}

// Read trait
let mut buffer = [0; 10];
file.read(&mut buffer)?;

// Write trait
file.write_all(b"data")?;

Networking

use std::net::{TcpListener, TcpStream, UdpSocket};
use std::io::{Read, Write};

// TCP Server
let listener = TcpListener::bind("127.0.0.1:8080")?;
for stream in listener.incoming() {
    let mut stream = stream?;
    stream.write_all(b"HTTP/1.1 200 OK\r\n\r\n")?;
}

// TCP Client
let mut stream = TcpStream::connect("127.0.0.1:8080")?;
stream.write_all(b"GET / HTTP/1.1\r\n\r\n")?;

let mut buffer = [0; 512];
let n = stream.read(&mut buffer)?;

// UDP Socket
let socket = UdpSocket::bind("127.0.0.1:3400")?;
socket.send_to(b"hello", "127.0.0.1:3401")?;

let mut buf = [0; 10];
let (amt, src) = socket.recv_from(&mut buf)?;

Concurrency and Threading

Threading

use std::thread;
use std::time::Duration;

// Spawn a thread
let handle = thread::spawn(|| {
    for i in 1..10 {
        println!("Thread: {}", i);
        thread::sleep(Duration::from_millis(1));
    }
});

// Wait for completion
handle.join().unwrap();

// Move data into thread
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
    println!("Vector: {:?}", v);
});

// Named threads
let builder = thread::Builder::new()
    .name("worker".into())
    .stack_size(4 * 1024 * 1024);

let handle = builder.spawn(|| {
    println!("Working...");
})?;

Synchronization Primitives

use std::sync::{Arc, Mutex, RwLock};
use std::thread;

// Mutex for exclusive access
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];

for _ in 0..10 {
    let counter = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1;
    });
    handles.push(handle);
}

for handle in handles {
    handle.join().unwrap();
}

println!("Result: {}", *counter.lock().unwrap());

// RwLock for reader-writer access
let lock = RwLock::new(5);

{
    let r1 = lock.read().unwrap();
    let r2 = lock.read().unwrap();
    assert_eq!(*r1, 5);
} // Read locks dropped

{
    let mut w = lock.write().unwrap();
    *w += 1;
}
use std::sync::mpsc;
use std::thread;

// Multiple producer, single consumer
let (tx, rx) = mpsc::channel();

thread::spawn(move || {
    tx.send(42).unwrap();
});

let received = rx.recv().unwrap();
println!("Got: {}", received);

// Multiple senders
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();

thread::spawn(move || tx.send(1).unwrap());
thread::spawn(move || tx1.send(2).unwrap());

for received in rx {
    println!("Got: {}", received);
}
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;

let flag = Arc::new(AtomicBool::new(false));
let counter = Arc::new(AtomicUsize::new(0));

// Atomic operations
flag.store(true, Ordering::SeqCst);
let old_val = counter.fetch_add(1, Ordering::SeqCst);
let current = counter.load(Ordering::SeqCst);

// Compare-and-swap
let _ = counter.compare_exchange(
    5, 10, 
    Ordering::SeqCst, 
    Ordering::SeqCst
);

Process and Environment

use std::process::Command;

// Run a command
let output = Command::new("ls")
    .arg("-la")
    .output()
    .expect("Failed to execute command");

println!("Status: {}", output.status);
println!("Stdout: {}", String::from_utf8_lossy(&output.stdout));

// Spawn and interact
let mut child = Command::new("cat")
    .stdin(std::process::Stdio::piped())
    .spawn()?;

if let Some(mut stdin) = child.stdin.take() {
    stdin.write_all(b"Hello, cat!")?;
}

child.wait()?;

// Exit
std::process::exit(0);
use std::env;

// Get environment variable
let path = env::var("PATH")?;
let home = env::var("HOME").unwrap_or_else(|_| String::from("/"));

// Set environment variable
env::set_var("MY_VAR", "value");
env::remove_var("MY_VAR");

// Current directory
let current = env::current_dir()?;
env::set_current_dir("/tmp")?;

// Command-line arguments
let args: Vec<String> = env::args().collect();
for arg in env::args() {
    println!("Arg: {}", arg);
}

Time and Duration

use std::time::{Duration, Instant, SystemTime};
use std::thread;

// Duration
let duration = Duration::from_secs(5);
let duration = Duration::from_millis(100);
let duration = Duration::from_micros(50);

// Instant (monotonic clock)
let start = Instant::now();
thread::sleep(Duration::from_millis(100));
let elapsed = start.elapsed();
println!("Elapsed: {:?}", elapsed);

// SystemTime (wall clock)
let now = SystemTime::now();
let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH)?;
println!("Seconds since epoch: {}", since_epoch.as_secs());

Path Handling

use std::path::{Path, PathBuf};

// Creating paths
let path = Path::new("/tmp/file.txt");
let mut path_buf = PathBuf::from("/tmp");
path_buf.push("file.txt");

// Path components
let extension = path.extension(); // Some("txt")
let file_name = path.file_name(); // Some("file.txt")
let parent = path.parent(); // Some("/tmp")
let file_stem = path.file_stem(); // Some("file")

// Path manipulation
let joined = path.join("subdir");
let absolute = path.canonicalize()?;
let exists = path.exists();
let is_file = path.is_file();
let is_dir = path.is_dir();

// Iteration
for component in path.components() {
    println!("{:?}", component);
}

String Operations

// String creation
let s = String::from("hello");
let s = "hello".to_string();
let s = format!("x = {}", 10);

// String manipulation
let mut s = String::new();
s.push_str("hello");
s.push(' ');
s.push_str("world");

// Concatenation
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // s1 moved
let s = format!("{}{}", s2, s3);

// String slicing
let hello = &s[0..5];
let world = &s[6..11];

// Iteration
for c in s.chars() {
    println!("{}", c);
}

for b in s.bytes() {
    println!("{}", b);
}

Error Handling

use std::error::Error;
use std::fmt;

#[derive(Debug)]
struct MyError {
    details: String,
}

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MyError: {}", self.details)
    }
}

impl Error for MyError {}

// Using trait objects
fn do_something() -> Result<(), Box<dyn Error>> {
    let file = std::fs::File::open("missing.txt")?;
    Ok(())
}

// Error context
let result = do_something()
    .map_err(|e| format!("Failed to do something: {}", e));

Macros

// Printing
println!("Hello, world!");
println!("x = {}, y = {}", 10, 20);
eprintln!("Error: {}", error);

// String formatting
let s = format!("Value: {}", 42);
let s = format!("{:?}", some_struct); // Debug
let s = format!("{:#?}", some_struct); // Pretty debug

// Assertions
assert_eq!(2 + 2, 4);
assert_ne!(2 + 2, 5);
assert!(true);
debug_assert!(expensive_check()); // Only in debug builds

// Panicking
panic!("Something went wrong");
unreachable!("This code should never execute");
unimplemented!("TODO: implement this");
todo!("Not yet implemented");

// Vector creation
let v = vec![1, 2, 3];
let v = vec![0; 100]; // 100 zeros

Before and After main()

Many parts of the standard library are expected to work before and after main(), but this is not guaranteed or ensured by tests. Write your own tests for each platform you wish to support.Some known limitations:
  • After-main use of thread-locals
  • thread::current() may not work
  • Under UNIX, file descriptors 0, 1, 2 behavior before main

Stability

The standard library has been stable since Rust 1.0.0. It maintains strict backward compatibility and follows semantic versioning.
  • core - Dependency-free foundation
  • alloc - Heap allocation primitives
  • proc_macro - Procedural macro support

Build docs developers (and LLMs) love