Giao diện
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 version4. Error Handling: thiserror vs anyhow
| Crate | Use Case | Purpose |
|---|---|---|
| thiserror | Libraries | Define custom error types |
| anyhow | Applications | Propagate 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 successfulJSON 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
| Crate | Category | Key Feature |
|---|---|---|
| serde | Serialization | #[derive(Serialize, Deserialize)] |
| rayon | Parallelism | .par_iter() thay .iter() |
| clap | CLI | #[derive(Parser)] |
| thiserror | Error (Lib) | #[derive(Error)] |
| anyhow | Error (App) | .context(), bail!, ensure! |
| tracing | Logging | Structured, async-aware, spans |