Popular Crates Reference
A guide to commonly used Rust crates with examples and use cases.
Table of Contents
- Serialization
- Async Runtime
- CLI Tools
- HTTP Clients
- Web Frameworks
- Database Access
- Logging and Tracing
- Error Handling
- Testing
- Configuration
- Date and Time
- Regular Expressions
- Cryptography
- Utilities
Serialization
serde - Serialization/Deserialization Framework
Purpose: Universal serialization framework for Rust
Installation:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
When to Use:
- Converting Rust data structures to/from JSON, YAML, TOML, etc.
- Working with APIs
- Configuration files
- Data persistence
Basic Example:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct User {
name: String,
age: u32,
email: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Serialize to JSON
let user = User {
name: "Alice".to_string(),
age: 30,
email: "[email protected]".to_string(),
};
let json = serde_json::to_string(&user)?;
println!("JSON: {}", json);
// Deserialize from JSON
let user2: User = serde_json::from_str(&json)?;
println!("User: {:?}", user2);
Ok(())
}
Advanced Features:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Config {
#[serde(rename = "serverPort")]
server_port: u16,
#[serde(default = "default_timeout")]
timeout: u64,
#[serde(skip_serializing_if = "Option::is_none")]
api_key: Option<String>,
#[serde(flatten)]
extra: std::collections::HashMap<String, serde_json::Value>,
}
fn default_timeout() -> u64 {
30
}
Related Crates:
serde_json: JSON supportserde_yaml: YAML supporttoml: TOML supportbincode: Binary encoding
Async Runtime
tokio - Async Runtime
Purpose: Asynchronous runtime for Rust
Installation:
[dependencies]
tokio = { version = "1.0", features = ["full"] }
When to Use:
- Building async applications
- Network servers
- Concurrent I/O operations
- High-performance services
Basic Example:
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
println!("Starting...");
// Spawn concurrent tasks
let task1 = tokio::spawn(async {
sleep(Duration::from_secs(1)).await;
println!("Task 1 done");
});
let task2 = tokio::spawn(async {
sleep(Duration::from_secs(2)).await;
println!("Task 2 done");
});
// Wait for both tasks
let _ = tokio::join!(task1, task2);
println!("All tasks complete");
}
TCP Server Example:
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("Server listening on 8080");
loop {
let (socket, addr) = listener.accept().await?;
println!("New connection from: {}", addr);
tokio::spawn(async move {
handle_client(socket).await;
});
}
}
async fn handle_client(mut socket: TcpStream) {
let mut buf = [0; 1024];
match socket.read(&mut buf).await {
Ok(n) if n > 0 => {
socket.write_all(&buf[..n]).await.ok();
}
_ => {}
}
}
Common Features:
tokio = { version = "1.0", features = [
"rt-multi-thread", # Multi-threaded runtime
"macros", # #[tokio::main] and #[tokio::test]
"fs", # Async filesystem operations
"io-util", # AsyncReadExt, AsyncWriteExt
"net", # TCP, UDP networking
"time", # Timers and sleep
"sync", # Async synchronization primitives
] }
async-std - Alternative Async Runtime
Purpose: Standard library-like async runtime
Installation:
[dependencies]
async-std = { version = "1.12", features = ["attributes"] }
Example:
use async_std::task;
use async_std::net::TcpListener;
#[async_std::main]
async fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("Server listening");
Ok(())
}
CLI Tools
clap - Command Line Argument Parser
Purpose: Parse command-line arguments
Installation:
[dependencies]
clap = { version = "4.0", features = ["derive"] }
When to Use:
- Building CLI applications
- Parsing command-line arguments
- Generating help messages
- Validating input
Basic Example:
use clap::Parser;
#[derive(Parser, Debug)]
#[command(name = "myapp")]
#[command(about = "A simple CLI app", long_about = None)]
struct Args {
/// Name of the person to greet
#[arg(short, long)]
name: String,
/// Number of times to greet
#[arg(short, long, default_value_t = 1)]
count: u8,
/// Enable verbose mode
#[arg(short, long)]
verbose: bool,
}
fn main() {
let args = Args::parse();
for _ in 0..args.count {
println!("Hello, {}!", args.name);
}
if args.verbose {
println!("Verbose mode enabled");
}
}
Advanced Example:
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "git")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Add files to staging
Add {
/// Files to add
files: Vec<String>,
},
/// Commit changes
Commit {
/// Commit message
#[arg(short, long)]
message: String,
},
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Add { files } => {
println!("Adding files: {:?}", files);
}
Commands::Commit { message } => {
println!("Committing with message: {}", message);
}
}
}
HTTP Clients
reqwest - HTTP Client
Purpose: High-level HTTP client
Installation:
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1.0", features = ["full"] }
When to Use:
- Making HTTP requests
- Calling REST APIs
- Downloading files
- Web scraping
Basic Example:
use reqwest;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// GET request
let response = reqwest::get("https://api.github.com/users/rust-lang")
.await?
.text()
.await?;
println!("Response: {}", response);
// POST request with JSON
let client = reqwest::Client::new();
let res = client
.post("https://httpbin.org/post")
.json(&serde_json::json!({
"name": "Alice",
"age": 30
}))
.send()
.await?;
println!("Status: {}", res.status());
Ok(())
}
Advanced Example:
use reqwest::{Client, header};
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct User {
login: String,
name: Option<String>,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::builder()
.user_agent("my-app/1.0")
.timeout(std::time::Duration::from_secs(10))
.build()?;
let user: User = client
.get("https://api.github.com/users/rust-lang")
.header(header::ACCEPT, "application/json")
.send()
.await?
.json()
.await?;
println!("User: {:?}", user);
Ok(())
}
ureq - Synchronous HTTP Client
Purpose: Simple synchronous HTTP client
Installation:
[dependencies]
ureq = { version = "2.9", features = ["json"] }
Example:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let response = ureq::get("https://httpbin.org/get")
.call()?
.into_string()?;
println!("{}", response);
Ok(())
}
Web Frameworks
axum - Ergonomic Web Framework
Purpose: Web application framework built on tokio
Installation:
[dependencies]
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }
Example:
use axum::{
routing::{get, post},
Router,
Json,
extract::Path,
};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct User {
name: String,
}
async fn hello() -> &'static str {
"Hello, World!"
}
async fn get_user(Path(id): Path<u32>) -> String {
format!("User ID: {}", id)
}
async fn create_user(Json(user): Json<User>) -> Json<User> {
Json(user)
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(hello))
.route("/users/:id", get(get_user))
.route("/users", post(create_user));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
println!("Server running on http://127.0.0.1:3000");
axum::serve(listener, app).await.unwrap();
}
actix-web - Powerful Web Framework
Purpose: High-performance web framework
Installation:
[dependencies]
actix-web = "4"
Example:
use actix_web::{web, App, HttpResponse, HttpServer};
async fn hello() -> HttpResponse {
HttpResponse::Ok().body("Hello world!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(hello))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Database Access
sqlx - Async SQL Toolkit
Purpose: Async SQL database access with compile-time query checking
Installation:
[dependencies]
sqlx = { version = "0.7", features = ["runtime-tokio-native-tls", "postgres"] }
tokio = { version = "1.0", features = ["full"] }
When to Use:
- Async database access
- Type-safe SQL queries
- Database migrations
- Connection pooling
Example:
use sqlx::postgres::PgPoolOptions;
use sqlx::FromRow;
#[derive(FromRow, Debug)]
struct User {
id: i32,
name: String,
email: String,
}
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
// Create connection pool
let pool = PgPoolOptions::new()
.max_connections(5)
.connect("postgres://user:pass@localhost/db").await?;
// Query with compile-time checking (requires DATABASE_URL env var)
let users: Vec<User> = sqlx::query_as!(
User,
"SELECT id, name, email FROM users WHERE id = $1",
1
)
.fetch_all(&pool)
.await?;
println!("Users: {:?}", users);
// Insert
sqlx::query!(
"INSERT INTO users (name, email) VALUES ($1, $2)",
"Alice",
"[email protected]"
)
.execute(&pool)
.await?;
Ok(())
}
diesel - ORM and Query Builder
Purpose: Safe, extensible ORM and query builder
Installation:
[dependencies]
diesel = { version = "2.1", features = ["postgres"] }
Example:
use diesel::prelude::*;
#[derive(Queryable)]
struct User {
id: i32,
name: String,
email: String,
}
fn main() {
use schema::users::dsl::*;
let connection = &mut establish_connection();
let results = users
.filter(name.eq("Alice"))
.limit(5)
.load::<User>(connection)
.expect("Error loading users");
for user in results {
println!("{} - {}", user.name, user.email);
}
}
Logging and Tracing
tracing - Application-level Tracing
Purpose: Structured logging and tracing
Installation:
[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"
Example:
use tracing::{info, warn, error, debug, trace, instrument};
use tracing_subscriber;
#[instrument]
fn process_data(id: u32) -> Result<(), String> {
debug!("Processing started");
if id == 0 {
error!("Invalid ID: {}", id);
return Err("Invalid ID".to_string());
}
info!("Processing data for ID: {}", id);
Ok(())
}
fn main() {
tracing_subscriber::fmt::init();
info!("Application started");
process_data(1).ok();
warn!("This is a warning");
}
log - Logging Facade
Purpose: Simple logging facade
Installation:
[dependencies]
log = "0.4"
env_logger = "0.11"
Example:
use log::{info, warn, error, debug};
fn main() {
env_logger::init();
debug!("Debug message");
info!("Info message");
warn!("Warning message");
error!("Error message");
}
Error Handling
anyhow - Flexible Error Handling
Purpose: Easy error handling for applications
Installation:
[dependencies]
anyhow = "1.0"
When to Use:
- Application code (not libraries)
- Quick prototyping
- When you don't need specific error types
Example:
use anyhow::{Result, Context, bail};
fn read_config(path: &str) -> Result<String> {
if path.is_empty() {
bail!("Path cannot be empty");
}
std::fs::read_to_string(path)
.context("Failed to read config file")
}
fn main() -> Result<()> {
let config = read_config("config.toml")?;
println!("Config: {}", config);
Ok(())
}
thiserror - Custom Error Types
Purpose: Derive macro for error types
Installation:
[dependencies]
thiserror = "1.0"
When to Use:
- Library code
- When you need specific error types
- Better error context
Example:
use thiserror::Error;
#[derive(Error, Debug)]
enum MyError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error: {0}")]
Parse(String),
#[error("Not found: {0}")]
NotFound(String),
#[error("Invalid data at line {line}: {msg}")]
Invalid { line: usize, msg: String },
}
fn parse_file(path: &str) -> Result<(), MyError> {
let content = std::fs::read_to_string(path)?;
if content.is_empty() {
return Err(MyError::Invalid {
line: 0,
msg: "Empty file".to_string(),
});
}
Ok(())
}
Testing
criterion - Benchmarking
Purpose: Statistical benchmarking library
Installation:
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "my_benchmark"
harness = false
Example:
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u64) -> u64 {
match n {
0 => 1,
1 => 1,
n => fibonacci(n-1) + fibonacci(n-2),
}
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
proptest - Property Testing
Purpose: Property-based testing framework
Installation:
[dev-dependencies]
proptest = "1.4"
Example:
use proptest::prelude::*;
fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
let mut rev = Vec::new();
for x in xs.iter().rev() {
rev.push(x.clone())
}
rev
}
proptest! {
#[test]
fn test_reverse(ref xs in prop::collection::vec(any::<i32>(), 0..100)) {
assert_eq!(reverse(&reverse(xs)), *xs);
}
}
Configuration
config - Configuration Management
Purpose: Layered configuration system
Installation:
[dependencies]
config = "0.14"
serde = { version = "1.0", features = ["derive"] }
Example:
use config::{Config, File};
use serde::Deserialize;
#[derive(Deserialize)]
struct AppConfig {
server: ServerConfig,
database: DatabaseConfig,
}
#[derive(Deserialize)]
struct ServerConfig {
host: String,
port: u16,
}
#[derive(Deserialize)]
struct DatabaseConfig {
url: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::builder()
.add_source(File::with_name("config"))
.add_source(config::Environment::with_prefix("APP"))
.build()?;
let app_config: AppConfig = config.try_deserialize()?;
println!("Server: {}:{}", app_config.server.host, app_config.server.port);
Ok(())
}
Date and Time
chrono - Date and Time
Purpose: Date and time handling
Installation:
[dependencies]
chrono = "0.4"
Example:
use chrono::{DateTime, Utc, Local, Duration};
fn main() {
// Current time
let now: DateTime<Utc> = Utc::now();
let local = Local::now();
println!("UTC: {}", now);
println!("Local: {}", local);
// Parsing
let dt = DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z").unwrap();
// Arithmetic
let tomorrow = now + Duration::days(1);
let week_ago = now - Duration::weeks(1);
// Formatting
let formatted = now.format("%Y-%m-%d %H:%M:%S");
println!("{}", formatted);
}
Regular Expressions
regex - Regular Expressions
Purpose: Regular expression matching
Installation:
[dependencies]
regex = "1"
Example:
use regex::Regex;
fn main() {
let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
if let Some(caps) = re.captures("Date: 2024-01-15") {
println!("Year: {}", &caps[1]);
println!("Month: {}", &caps[2]);
println!("Day: {}", &caps[3]);
}
// Replace
let result = re.replace_all("2024-01-15", "$1/$2/$3");
println!("{}", result); // 2024/01/15
}