Skip to content

The "Blessed" Crates Essential

Những crate được community công nhận là "chuẩn công nghiệp" — sử dụng trong 90% dự án Rust

Thế nào là "Blessed" Crate?

Rust không có standard library lớn như Python. Thay vào đó, community đã "vote" ra các crates được coi là de facto standard:

┌─────────────────────────────────────────────────────────────────────┐
│                    THE BLESSED CRATE ECOSYSTEM                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Category        │  Crate          │  Downloads        │  Status   │
│  ────────────────┼─────────────────┼───────────────────┼──────────│
│  Serialization   │  serde          │  350M+            │  ⭐ King  │
│  Parallelism     │  rayon          │  150M+            │  ⭐ King  │
│  CLI Parsing     │  clap           │  100M+            │  ⭐ King  │
│  Error (Lib)     │  thiserror      │  100M+            │  ⭐ King  │
│  Error (App)     │  anyhow         │  80M+             │  ⭐ King  │
│  Logging/Trace   │  tracing        │  100M+            │  ⭐ King  │
│  Async Runtime   │  tokio          │  200M+            │  ⭐ King  │
│  HTTP Client     │  reqwest        │  100M+            │  ⭐ King  │
│                                                                     │
│  Nếu bạn không biết dùng gì → Dùng crates này!                     │
└─────────────────────────────────────────────────────────────────────┘

1. Serde: Universal Serialization

Serde = Serialize + Deserialize. Framework cho JSON, YAML, TOML, Bincode, và 50+ formats.

Setup

toml
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"      # JSON format
# serde_yaml = "0.9"  # YAML format
# toml = "0.8"        # TOML format
# bincode = "1"       # Binary format (fast)

Basic Usage: Derive Macros

rust
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    name: String,
    email: String,
    #[serde(default)]  // Dùng Default::default() nếu missing
    active: bool,
}

fn main() -> Result<(), serde_json::Error> {
    // Serialize to JSON
    let user = User {
        id: 1,
        name: "Alice".to_string(),
        email: "alice@example.com".to_string(),
        active: true,
    };
    
    let json = serde_json::to_string_pretty(&user)?;
    println!("{}", json);
    // {
    //   "id": 1,
    //   "name": "Alice",
    //   "email": "alice@example.com",
    //   "active": true
    // }
    
    // Deserialize from JSON
    let json_str = r#"{"id": 2, "name": "Bob", "email": "bob@example.com"}"#;
    let user: User = serde_json::from_str(json_str)?;
    println!("{:?}", user);  // User { id: 2, name: "Bob", email: "bob@example.com", active: false }
    
    Ok(())
}

Advanced: Field Attributes

rust
#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse {
    #[serde(rename = "userId")]     // JSON field name khác Rust field
    user_id: u64,
    
    #[serde(skip_serializing_if = "Option::is_none")]  // Skip nếu None
    avatar_url: Option<String>,
    
    #[serde(flatten)]               // Flatten nested struct
    metadata: Metadata,
    
    #[serde(skip)]                  // Không serialize/deserialize
    internal_cache: String,
    
    #[serde(deserialize_with = "deserialize_timestamp")]  // Custom deserializer
    created_at: DateTime<Utc>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Metadata {
    version: String,
    source: String,
}

// Result:
// {
//   "userId": 123,
//   "avatarUrl": "https://...",  // Chỉ xuất hiện nếu Some
//   "version": "1.0",            // Flattened từ Metadata
//   "source": "api",
//   "createdAt": "2024-01-01T00:00:00Z"
// }

Enum Serialization

rust
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]  // Tagged enum representation
enum Event {
    #[serde(rename = "user_created")]
    UserCreated { user_id: u64 },
    
    #[serde(rename = "order_placed")]
    OrderPlaced { order_id: u64, total: f64 },
}

// Result:
// { "type": "user_created", "user_id": 123 }
// { "type": "order_placed", "order_id": 456, "total": 99.99 }

2. Rayon: Data Parallelism

Rayon biến sequential iterators thành parallel iterators với 1 method call.

Setup

toml
[dependencies]
rayon = "1"

One Line Parallelism

rust
use rayon::prelude::*;

fn main() {
    let numbers: Vec<i32> = (0..1_000_000).collect();
    
    // Sequential (single thread)
    let sum_seq: i32 = numbers.iter().map(|x| x * 2).sum();
    
    // Parallel (all cores) - CHỈ THAY iter() BẰNG par_iter()
    let sum_par: i32 = numbers.par_iter().map(|x| x * 2).sum();
    
    assert_eq!(sum_seq, sum_par);
}

Parallel Processing Examples

rust
use rayon::prelude::*;

// Parallel map
let results: Vec<_> = data
    .par_iter()
    .map(|item| expensive_computation(item))
    .collect();

// Parallel filter
let filtered: Vec<_> = items
    .par_iter()
    .filter(|x| x.is_valid())
    .cloned()
    .collect();

// Parallel for_each (side effects)
files.par_iter().for_each(|file| {
    process_file(file);
});

// Parallel sort
let mut data = vec![3, 1, 4, 1, 5, 9, 2, 6];
data.par_sort();

// Parallel find
let found = haystack.par_iter().find_any(|x| **x == needle);

Benchmark: Sequential vs Parallel

rust
use rayon::prelude::*;
use std::time::Instant;

fn benchmark() {
    let data: Vec<u64> = (0..10_000_000).collect();
    
    // Sequential
    let start = Instant::now();
    let _: u64 = data.iter().map(|x| fibonacci(*x % 30)).sum();
    println!("Sequential: {:?}", start.elapsed());
    
    // Parallel
    let start = Instant::now();
    let _: u64 = data.par_iter().map(|x| fibonacci(*x % 30)).sum();
    println!("Parallel:   {:?}", start.elapsed());
}

// Results (8-core CPU):
// Sequential: 4.2s
// Parallel:   0.6s  (7x speedup)

Configure Thread Pool

rust
use rayon::ThreadPoolBuilder;

fn main() {
    // Custom thread pool với 4 threads
    ThreadPoolBuilder::new()
        .num_threads(4)
        .build_global()
        .unwrap();
    
    // Tất cả par_iter() sau đây sẽ dùng 4 threads
}

3. Clap: CLI Argument Parsing

Clap = Command Line Argument Parser. De facto standard cho CLI tools.

Setup

toml
[dependencies]
clap = { version = "4", features = ["derive"] }

Derive-based CLI

rust
use clap::{Parser, Subcommand, ValueEnum};

#[derive(Parser)]
#[command(name = "myapp")]
#[command(author, version, about, long_about = None)]
struct Cli {
    /// Enable verbose output
    #[arg(short, long, global = true)]
    verbose: bool,
    
    /// Config file path
    #[arg(short, long, default_value = "config.toml")]
    config: String,
    
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Start the server
    Start {
        /// Port to listen on
        #[arg(short, long, default_value_t = 8080)]
        port: u16,
        
        /// Environment
        #[arg(short, long, value_enum, default_value_t = Environment::Development)]
        env: Environment,
    },
    
    /// Run database migrations
    Migrate {
        /// Migration direction
        #[arg(value_enum)]
        direction: Direction,
    },
}

#[derive(Clone, ValueEnum)]
enum Environment {
    Development,
    Staging,
    Production,
}

#[derive(Clone, ValueEnum)]
enum Direction {
    Up,
    Down,
}

fn main() {
    let cli = Cli::parse();
    
    if cli.verbose {
        println!("Verbose mode enabled");
    }
    
    match cli.command {
        Commands::Start { port, env } => {
            println!("Starting server on port {} ({:?})", port, env);
        }
        Commands::Migrate { direction } => {
            println!("Running migration: {:?}", direction);
        }
    }
}

Generated Help

$ myapp --help
Usage: myapp [OPTIONS] <COMMAND>

Commands:
  start    Start the server
  migrate  Run database migrations
  help     Print this message

Options:
  -v, --verbose          Enable verbose output
  -c, --config <CONFIG>  Config file path [default: config.toml]
  -h, --help             Print help
  -V, --version          Print version

4. Error Handling: thiserror vs anyhow

CrateUse CasePurpose
thiserrorLibrariesDefine custom error types
anyhowApplicationsPropagate any error easily

thiserror (cho Libraries)

toml
[dependencies]
thiserror = "1"
rust
use thiserror::Error;

#[derive(Debug, Error)]
pub enum DatabaseError {
    #[error("Connection failed: {0}")]
    Connection(String),
    
    #[error("Query failed: {query}")]
    Query { query: String, #[source] cause: sqlx::Error },
    
    #[error("Record not found: {0}")]
    NotFound(String),
    
    #[error(transparent)]  // Delegate Display to inner error
    Io(#[from] std::io::Error),
}

// Usage in library
pub fn get_user(id: u64) -> Result<User, DatabaseError> {
    let conn = connect().map_err(|e| DatabaseError::Connection(e.to_string()))?;
    
    conn.query("SELECT * FROM users WHERE id = ?", id)
        .map_err(|e| DatabaseError::Query {
            query: "SELECT...".into(),
            cause: e,
        })?
        .ok_or_else(|| DatabaseError::NotFound(format!("User {}", id)))
}
)}}

anyhow (cho Applications)

toml
[dependencies]
anyhow = "1"
rust
use anyhow::{Context, Result, bail, ensure};

// Result = anyhow::Result<T> = Result<T, anyhow::Error>

fn main() -> Result<()> {
    let config = load_config()
        .context("Failed to load configuration")?;  // Add context to error
    
    let db = connect_database(&config.db_url)
        .with_context(|| format!("Failed to connect to {}", config.db_url))?;
    
    // bail! = return Err immediately
    if config.api_key.is_empty() {
        bail!("API key is not configured");
    }
    
    // ensure! = assert that returns Err
    ensure!(config.port > 0, "Port must be positive");
    
    run_server(db, config)?;
    
    Ok(())
}

// Error chain được preserve:
// Error: Failed to load configuration
// Caused by:
//     0: Failed to read config.toml
//     1: No such file or directory (os error 2)

5. Tracing: Structured Logging

Tracing = Structured, async-aware logging. Thay thế log crate cho modern applications.

Setup

toml
[dependencies]
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

Basic Usage

rust
use tracing::{info, warn, error, debug, trace, instrument, span, Level};

fn main() {
    // Initialize subscriber (logger)
    tracing_subscriber::fmt()
        .with_env_filter("myapp=debug,tower_http=debug")
        .init();
    
    info!("Application starting");
    
    let user_id = 42;
    info!(user_id, "User logged in");  // Structured field
    
    warn!(attempts = 3, "Rate limit approaching");
    
    error!(error = ?some_error, "Failed to process request");
}

Spans: Tracking Operations

rust
use tracing::{info_span, Instrument};

async fn handle_request(req: Request) -> Response {
    // Create span for this request
    let span = info_span!(
        "handle_request",
        method = %req.method(),
        path = %req.uri().path(),
        request_id = %uuid::Uuid::new_v4(),
    );
    
    async move {
        info!("Processing request");
        
        let result = process(req).await;
        
        info!(status = %result.status(), "Request completed");
        
        result
    }
    .instrument(span)  // Attach span to async block
    .await
}

#[instrument] Attribute

rust
use tracing::instrument;

#[instrument(skip(password), fields(user_id))]  // Skip sensitive fields
async fn login(username: &str, password: &str) -> Result<User, AuthError> {
    let user = find_user(username).await?;
    
    tracing::Span::current().record("user_id", user.id);  // Add field later
    
    verify_password(&user, password)?;
    
    Ok(user)
}

// Output:
// INFO login{username="alice" user_id=42}: myapp: Login successful

JSON Output (Production)

rust
use tracing_subscriber::fmt::format::FmtSpan;

fn init_tracing() {
    tracing_subscriber::fmt()
        .json()  // JSON format
        .with_span_events(FmtSpan::CLOSE)  // Log span durations
        .with_current_span(true)
        .init();
}

// Output:
// {"timestamp":"2024-01-01T00:00:00Z","level":"INFO","message":"Request completed","target":"myapp","span":{"name":"handle_request","method":"GET","path":"/api/users"},"duration_ms":42}

Bảng Tóm tắt

CrateCategoryKey Feature
serdeSerialization#[derive(Serialize, Deserialize)]
rayonParallelism.par_iter() thay .iter()
clapCLI#[derive(Parser)]
thiserrorError (Lib)#[derive(Error)]
anyhowError (App).context(), bail!, ensure!
tracingLoggingStructured, async-aware, spans