A collection of practical Rust code examples covering common patterns and use cases.
Table of Contents#
- CLI Tool (Argument Parsing + Output)
- File I/O (Read, Write, Append)
- HTTP Server (Blocking, Minimal)
- Concurrency (Threads + Channels)
- Concurrency (Shared State with Arc + Mutex)
- Error Handling Patterns
- Struct + Trait + Impl (Complete Example)
- Iterator Chaining
- Troubleshooting
- See also
- Sources
CLI Tool (Argument Parsing + Output)#
A minimal command-line tool that reads a file and counts lines, words, or characters.
use std::fs;
use std::env;
use std::process;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
eprintln!("Usage: {} <mode> <file>", args[0]);
eprintln!(" mode: lines | words | chars");
process::exit(1);
}
let mode = &args[1];
let path = &args[2];
let content = match fs::read_to_string(path) {
Ok(c) => c,
Err(e) => {
eprintln!("Error reading {}: {}", path, e);
process::exit(1);
}
};
let count = match mode.as_str() {
"lines" => content.lines().count(),
"words" => content.split_whitespace().count(),
"chars" => content.chars().count(),
other => {
eprintln!("Unknown mode: {}. Use lines, words, or chars.", other);
process::exit(1);
}
};
println!("{}: {}", mode, count);
}File I/O (Read, Write, Append)#
Reading and writing files with proper error handling.
use std::fs::{self, File, OpenOptions};
use std::io::{self, BufRead, BufReader, Write};
fn read_lines(path: &str) -> io::Result<Vec<String>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
reader.lines().collect()
}
fn write_file(path: &str, content: &str) -> io::Result<()> {
fs::write(path, content)
}
fn append_line(path: &str, line: &str) -> io::Result<()> {
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(path)?;
writeln!(file, "{}", line)
}
fn main() -> io::Result<()> {
// Write
write_file("example.txt", "line one\nline two\n")?;
// Append
append_line("example.txt", "line three")?;
// Read
let lines = read_lines("example.txt")?;
for (i, line) in lines.iter().enumerate() {
println!("{}: {}", i + 1, line);
}
// Cleanup
fs::remove_file("example.txt")?;
Ok(())
}HTTP Server (Blocking, Minimal)#
A tiny TCP-based HTTP server using only the standard library.
use std::io::{Read, Write};
use std::net::TcpListener;
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("Listening on http://127.0.0.1:8080");
for stream in listener.incoming() {
let mut stream = stream?;
let mut buffer = [0u8; 1024];
stream.read(&mut buffer)?;
let request = String::from_utf8_lossy(&buffer);
let path = request
.lines()
.next()
.unwrap_or("")
.split_whitespace()
.nth(1)
.unwrap_or("/");
let (status, body) = match path {
"/" => ("200 OK", "Hello, world!"),
"/health" => ("200 OK", "ok"),
_ => ("404 Not Found", "Not Found"),
};
let response = format!(
"HTTP/1.1 {}\r\nContent-Length: {}\r\n\r\n{}",
status,
body.len(),
body
);
stream.write_all(response.as_bytes())?;
}
Ok(())
}Concurrency (Threads + Channels)#
Spawning threads and communicating with channels.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
let num_workers = 4;
for id in 0..num_workers {
let tx = tx.clone();
thread::spawn(move || {
let result = id * id;
thread::sleep(Duration::from_millis(100));
tx.send((id, result)).unwrap();
});
}
// Drop the original sender so rx knows when all senders are gone
drop(tx);
for (id, result) in rx {
println!("Worker {} produced: {}", id, result);
}
println!("All workers finished.");
}Concurrency (Shared State with Arc + Mutex)#
Sharing mutable state across threads safely.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0u64));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..1000 {
let mut num = counter.lock().unwrap();
*num += 1;
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Final count: {}", *counter.lock().unwrap());
// Always prints 10000
}Error Handling Patterns#
Combining thiserror for library errors and ? propagation.
use std::fs;
use std::io;
use std::num::ParseIntError;
// Custom error enum (in a real project, use thiserror derive)
#[derive(Debug)]
enum AppError {
Io(io::Error),
Parse(ParseIntError),
Validation(String),
}
impl std::fmt::Display for AppError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
AppError::Io(e) => write!(f, "IO error: {}", e),
AppError::Parse(e) => write!(f, "Parse error: {}", e),
AppError::Validation(msg) => write!(f, "Validation: {}", msg),
}
}
}
impl From<io::Error> for AppError {
fn from(e: io::Error) -> Self { AppError::Io(e) }
}
impl From<ParseIntError> for AppError {
fn from(e: ParseIntError) -> Self { AppError::Parse(e) }
}
fn read_age(path: &str) -> Result<u8, AppError> {
let content = fs::read_to_string(path)?; // io::Error -> AppError
let age: u8 = content.trim().parse()?; // ParseIntError -> AppError
if age > 150 {
return Err(AppError::Validation("age too high".to_string()));
}
Ok(age)
}
fn main() {
match read_age("age.txt") {
Ok(age) => println!("Age: {}", age),
Err(e) => eprintln!("Failed: {}", e),
}
}Struct + Trait + Impl (Complete Example)#
Defining behavior through traits with multiple implementations.
trait Vehicle {
fn drive(&self);
fn stop(&self);
fn fuel_type(&self) -> &'static str;
fn wheels(&self) -> u8;
}
struct Car {
fuel: &'static str,
wheels: u8,
}
impl Vehicle for Car {
fn drive(&self) { println!("The car is driving."); }
fn stop(&self) { println!("The car stopped."); }
fn fuel_type(&self) -> &'static str { self.fuel }
fn wheels(&self) -> u8 { self.wheels }
}
struct Bike {
wheels: u8,
}
impl Vehicle for Bike {
fn drive(&self) { println!("The bike is being ridden."); }
fn stop(&self) { println!("The bike stopped."); }
fn fuel_type(&self) -> &'static str { "none" }
fn wheels(&self) -> u8 { self.wheels }
}
fn describe(v: &dyn Vehicle) {
v.drive();
println!(" Fuel: {}, Wheels: {}", v.fuel_type(), v.wheels());
v.stop();
}
fn main() {
let car = Car { fuel: "gasoline", wheels: 4 };
let bike = Bike { wheels: 2 };
describe(&car);
describe(&bike);
}Iterator Chaining#
Practical examples of composing iterator adaptors.
fn main() {
// Word frequency count
let text = "the quick brown fox jumps over the lazy dog the fox";
let mut freq = std::collections::HashMap::new();
for word in text.split_whitespace() {
*freq.entry(word).or_insert(0u32) += 1;
}
// Sort by frequency descending, then alphabetically
let mut sorted: Vec<_> = freq.iter().collect();
sorted.sort_by(|a, b| b.1.cmp(a.1).then(a.0.cmp(b.0)));
for (word, count) in &sorted {
println!("{:>4} {}", count, word);
}
// Pipeline: filter, transform, collect
let numbers: Vec<i32> = (1..=20)
.filter(|n| n % 3 == 0)
.map(|n| n * n)
.collect();
println!("Squares of multiples of 3: {:?}", numbers);
}Troubleshooting#
| Issue | Cause | Solution |
|---|---|---|
unused variable or unused import warnings | Declared but never used | Prefix with _ (e.g. _x) or remove the item |
cannot find value in this scope | Variable declared in a different block or scope | Move the declaration to an enclosing scope or pass it as a parameter |
mismatched types in match arms | Different branches return different types | Ensure all arms return the same type, or use an enum to wrap variants |
borrow of moved value in a loop | Value consumed in first iteration | Clone, borrow with &, or restructure the loop |
unused Result that must be used | Ignoring a Result return value | Handle with ?, match, or explicitly let _ = ... to suppress |