Rust 语言常用第三方库指南
Rust 语言以其内存安全、零成本抽象和出色的性能而闻名。除了强大的标准库外,Rust 还拥有丰富的 crates 生态系统。本文将介绍各类常用第三方库,帮助你更高效地开发 Rust 应用。
Web 框架
Axum
Axum 是 Tokio 团队开发的 Web 框架,与 Tokio 生态系统深度集成。
use axum::{
routing::{get, post},
Router,
extract::{Path, Json},
response::Json as ResponseJson,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
#[derive(Serialize)]
struct HelloResponse {
message: String,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Serialize)]
struct User {
id: u64,
name: String,
email: String,
}
async fn hello() -> ResponseJson<HelloResponse> {
ResponseJson(HelloResponse {
message: "Hello, World!".to_string(),
})
}
async fn get_user(Path(id): Path<u64>) -> ResponseJson<User> {
ResponseJson(User {
id,
name: "张三".to_string(),
email: "zhangsan@example.com".to_string(),
})
}
async fn create_user(Json(payload): Json<CreateUser>) -> ResponseJson<User> {
ResponseJson(User {
id: 1,
name: payload.name,
email: payload.email,
})
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(hello))
.route("/users/:id", get(get_user))
.route("/users", post(create_user));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}Actix-web
Actix-web 是性能极高的 Web 框架,基于 Actor 模型。
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
struct HelloResponse {
message: String,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Serialize)]
struct User {
id: u64,
name: String,
email: String,
}
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().json(HelloResponse {
message: "Hello, World!".to_string(),
})
}
#[get("/users/{id}")]
async fn get_user(path: web::Path<u64>) -> impl Responder {
let id = path.into_inner();
HttpResponse::Ok().json(User {
id,
name: "张三".to_string(),
email: "zhangsan@example.com".to_string(),
})
}
#[post("/users")]
async fn create_user(body: web::Json<CreateUser>) -> impl Responder {
HttpResponse::Created().json(User {
id: 1,
name: body.name.clone(),
email: body.email.clone(),
})
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(hello)
.service(get_user)
.service(create_user)
})
.bind("127.0.0.1:8080")?
.run()
.await
}Rocket
Rocket 是注重易用性和类型安全的 Web 框架。
#[macro_use]
extern crate rocket;
use rocket::serde::{json::Json, Deserialize, Serialize};
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
struct Message {
message: String,
}
#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
struct UserInput {
name: String,
age: u8,
}
#[get("/")]
fn index() -> Json<Message> {
Json(Message {
message: "Hello, Rocket!".to_string(),
})
}
#[get("/hello/<name>")]
fn hello(name: &str) -> String {
format!("Hello, {}!", name)
}
#[post("/user", format = "json", data = "<input>")]
fn create_user(input: Json<UserInput>) -> String {
format!("Created user: {} (age: {})", input.name, input.age)
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![index, hello, create_user])
}异步运行时
Tokio
Tokio 是 Rust 最流行的异步运行时,提供 I/O、调度、定时器等功能。
use tokio::time::{sleep, Duration};
use tokio::task;
#[tokio::main]
async fn main() {
// 创建并发任务
let task1 = task::spawn(async {
println!("Task 1 started");
sleep(Duration::from_secs(1)).await;
println!("Task 1 completed");
42
});
let task2 = task::spawn(async {
println!("Task 2 started");
sleep(Duration::from_millis(500)).await;
println!("Task 2 completed");
"Hello"
});
// 等待所有任务完成
let result1 = task1.await.unwrap();
let result2 = task2.await.unwrap();
println!("Results: {}, {}", result1, result2);
// TCP 服务器示例
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
println!("Server listening on 127.0.0.1:8080");
loop {
let (mut socket, _) = listener.accept().await.unwrap();
tokio::spawn(async move {
let mut buf = [0; 1024];
match socket.read(&mut buf).await {
Ok(n) if n == 0 => return,
Ok(n) => {
socket.write_all(&buf[0..n]).await.unwrap();
}
Err(e) => {
eprintln!("Failed to read from socket: {}", e);
}
}
});
}
}async-std
async-std 提供类似标准库的异步 API。
use async_std::task;
use async_std::fs::File;
use async_std::io::prelude::*;
use async_std::net::TcpListener;
#[async_std::main]
async fn main() -> std::io::Result<()> {
// 异步文件操作
let mut file = File::create("example.txt").await?;
file.write_all(b"Hello, async-std!").await?;
let mut file = File::open("example.txt").await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
println!("File contents: {}", contents);
// TCP 服务器
let listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("Listening on 127.0.0.1:8080");
while let Ok((mut stream, _)) = listener.accept().await {
task::spawn(async move {
let mut buf = vec![0u8; 1024];
if let Ok(n) = stream.read(&mut buf).await {
stream.write_all(&buf[..n]).await.unwrap();
}
});
}
Ok(())
}数据库
Diesel
Diesel 是安全的 ORM 和查询构建器。
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use diesel::Queryable;
// 定义模型
#[derive(Queryable)]
struct User {
id: i32,
name: String,
email: String,
}
#[derive(Insertable)]
#[table_name = "users"]
struct NewUser {
name: String,
email: String,
}
// 定义表结构
table! {
users (id) {
id -> Integer,
name -> Text,
email -> Text,
}
}
fn establish_connection() -> SqliteConnection {
SqliteConnection::establish("database.sqlite")
.expect("Error connecting to database")
}
fn main() {
use self::users::dsl::*;
let connection = establish_connection();
// 插入数据
let new_user = NewUser {
name: "张三".to_string(),
email: "zhangsan@example.com".to_string(),
};
diesel::insert_into(users)
.values(&new_user)
.execute(&connection)
.expect("Error saving new user");
// 查询数据
let results = users
.filter(name.like("%张%"))
.limit(5)
.load::<User>(&connection)
.expect("Error loading users");
println!("找到 {} 个用户", results.len());
for user in results {
println!("{}: {} ({})", user.id, user.name, user.email);
}
// 更新数据
diesel::update(users.find(1))
.set(name.eq("李四"))
.execute(&connection)
.expect("Error updating user");
// 删除数据
diesel::delete(users.filter(id.eq(1)))
.execute(&connection)
.expect("Error deleting user");
}sqlx
sqlx 是异步的、编译时检查的 SQL 库。
use sqlx::{sqlite::SqlitePool, Row};
#[derive(Debug)]
struct User {
id: i64,
name: String,
email: String,
}
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
// 创建连接池
let pool = SqlitePool::connect("sqlite://database.sqlite").await?;
// 创建表
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL
)
"#
)
.execute(&pool)
.await?;
// 插入数据
let user_id: i64 = sqlx::query(
"INSERT INTO users (name, email) VALUES (?, ?) RETURNING id"
)
.bind("张三")
.bind("zhangsan@example.com")
.fetch_one(&pool)
.await?
.get(0);
println!("Created user with id: {}", user_id);
// 查询单行
let user: User = sqlx::query_as(
"SELECT id, name, email FROM users WHERE id = ?"
)
.bind(user_id)
.fetch_one(&pool)
.await?;
println!("User: {:?}", user);
// 查询多行
let users: Vec<User> = sqlx::query_as(
"SELECT id, name, email FROM users"
)
.fetch_all(&pool)
.await?;
for user in users {
println!("User: {:?}", user);
}
Ok(())
}redis-rs
redis-rs 是 Redis 的 Rust 客户端。
use redis::{AsyncCommands, Client};
#[tokio::main]
async fn main() -> redis::RedisResult<()> {
// 创建客户端
let client = Client::open("redis://127.0.0.1:6379/")?;
let mut con = client.get_async_connection().await?;
// 设置键值
con.set("my_key", "my_value").await?;
// 获取值
let value: String = con.get("my_key").await?;
println!("my_key: {}", value);
// 设置过期时间
con.set_ex("temp_key", "temp_value", 60).await?;
// 使用哈希
con.hset("user:1", "name", "张三").await?;
con.hset("user:1", "age", 25).await?;
let name: String = con.hget("user:1", "name").await?;
println!("User name: {}", name);
// 列表操作
con.lpush("my_list", "item1").await?;
con.lpush("my_list", "item2").await?;
let item: String = con.rpop("my_list").await?;
println!("Popped item: {}", item);
// 发布订阅
let mut pubsub = con.into_pubsub();
pubsub.subscribe("my_channel").await?;
Ok(())
}序列化
Serde
Serde 是 Rust 的序列化和反序列化框架。
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Serialize, Deserialize, Debug)]
struct User {
name: String,
email: String,
age: u8,
#[serde(default)]
address: Option<String>,
#[serde(rename = "user_id")]
id: u64,
}
fn main() {
// 序列化为 JSON
let user = User {
name: "张三".to_string(),
email: "zhangsan@example.com".to_string(),
age: 25,
address: Some("北京市".to_string()),
id: 1,
};
let json = serde_json::to_string(&user).unwrap();
println!("JSON: {}", json);
// 反序列化
let json_str = r#"{
"name": "李四",
"email": "lisi@example.com",
"age": 30,
"user_id": 2
}"#;
let user: User = serde_json::from_str(json_str).unwrap();
println!("User: {:?}", user);
// 序列化为其他格式
// YAML
let yaml = serde_yaml::to_string(&user).unwrap();
println!("YAML:\n{}", yaml);
// TOML
let toml = toml::to_string(&user).unwrap();
println!("TOML:\n{}", toml);
// MessagePack
let msgpack = rmp_serde::to_vec(&user).unwrap();
println!("MessagePack length: {}", msgpack.len());
}错误处理
thiserror
thiserror 用于定义错误类型。
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MyError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error: {0}")]
Parse(#[from] std::num::ParseIntError),
#[error("Custom error: {message}")]
Custom { message: String },
#[error("Not found: {0}")]
NotFound(String),
}
fn may_fail() -> Result<i32, MyError> {
// 自动转换错误类型
let content = std::fs::read_to_string("file.txt")?;
let num: i32 = content.trim().parse()?;
if num < 0 {
return Err(MyError::Custom {
message: "Number must be positive".to_string(),
});
}
Ok(num)
}
fn find_user(id: u64) -> Result<User, MyError> {
if id == 0 {
return Err(MyError::NotFound(format!("User with id {}", id)));
}
Ok(User {
id,
name: "张三".to_string(),
})
}anyhow
anyhow 用于简化错误处理。
use anyhow::{Context, Result};
fn read_config() -> Result<String> {
// 自动转换任何错误
let config = std::fs::read_to_string("config.json")
.context("Failed to read config file")?;
Ok(config)
}
fn parse_number(s: &str) -> Result<i32> {
let num: i32 = s.parse()
.with_context(|| format!("Failed to parse '{}' as number", s))?;
Ok(num)
}
fn main() -> Result<()> {
let config = read_config()?;
let number = parse_number("42")?;
println!("Config: {}, Number: {}", config, number);
// 添加上下文
let result = some_operation()
.context("Operation failed")?;
Ok(())
}
fn some_operation() -> Result<()> {
anyhow::bail!("Something went wrong");
}HTTP 客户端
reqwest
reqwest 是易用的 HTTP 客户端。
use reqwest;
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
struct CreateUserRequest {
name: String,
email: String,
}
#[derive(Deserialize, Debug)]
struct UserResponse {
id: u64,
name: String,
email: String,
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = reqwest::Client::new();
// GET 请求
let response: serde_json::Value = client
.get("https://api.github.com/users/rust-lang")
.header("User-Agent", "reqwest")
.send()
.await?
.json()
.await?;
println!("Response: {:?}", response);
// POST JSON
let user = CreateUserRequest {
name: "张三".to_string(),
email: "zhangsan@example.com".to_string(),
};
let created: UserResponse = client
.post("https://api.example.com/users")
.json(&user)
.send()
.await?
.json()
.await?;
println!("Created user: {:?}", created);
// 表单提交
let params = [("key", "value"), ("foo", "bar")];
let response = client
.post("https://httpbin.org/post")
.form(¶ms)
.send()
.await?;
// 下载文件
let bytes = client
.get("https://example.com/file.pdf")
.send()
.await?
.bytes()
.await?;
std::fs::write("file.pdf", &bytes)?;
Ok(())
}日志
tracing
tracing 是异步感知的结构化日志框架。
use tracing::{info, warn, error, debug, span, Level};
use tracing_subscriber;
#[tracing::instrument]
async fn process_user(user_id: u64) -> Result<(), String> {
info!(user_id, "Processing user");
// 模拟异步操作
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
if user_id == 0 {
error!(user_id, "Invalid user ID");
return Err("Invalid user ID".to_string());
}
info!(user_id, "User processed successfully");
Ok(())
}
#[tokio::main]
async fn main() {
// 初始化订阅者
tracing_subscriber::fmt::init();
// 创建 span
let span = span!(Level::INFO, "main", version = "1.0");
let _enter = span.enter();
info!("Application started");
// 带字段的日志
info!(
target: "app",
user_count = 42,
"Users loaded"
);
// 异步函数自动追踪
if let Err(e) = process_user(123).await {
error!(error = %e, "Failed to process user");
}
// 手动创建 span
let db_span = span!(Level::DEBUG, "database_query", query = "SELECT * FROM users");
{
let _enter = db_span.enter();
debug!("Executing query");
// 数据库操作
debug!("Query completed");
}
}log + env_logger
标准日志接口的简单实现。
use log::{info, debug, error, warn};
fn main() {
// 初始化日志
env_logger::init();
// 不同级别的日志
debug!("This is a debug message");
info!("Application started");
warn!("This is a warning");
error!("This is an error: {}", "something failed");
// 带目标模块的日志
log::info!(target: "database", "Connected to database");
}CLI 工具
clap
clap 是强大的命令行参数解析器。
use clap::{Parser, Subcommand, Args};
#[derive(Parser)]
#[command(name = "myapp")]
#[command(about = "A CLI application")]
#[command(version = "1.0")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// 添加新用户
Add(AddArgs),
/// 列出所有用户
List,
/// 删除用户
Delete { id: u64 },
}
#[derive(Args)]
struct AddArgs {
/// 用户名
name: String,
/// 邮箱地址
#[arg(short, long)]
email: Option<String>,
/// 是否设为管理员
#[arg(short, long)]
admin: bool,
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Add(args) => {
println!("Adding user: {}", args.name);
if let Some(email) = args.email {
println!("Email: {}", email);
}
if args.admin {
println!("Admin: true");
}
}
Commands::List => {
println!("Listing all users");
}
Commands::Delete { id } => {
println!("Deleting user with ID: {}", id);
}
}
}structopt (已合并到 clap v3)
使用派生宏定义 CLI。
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(name = "example")]
#[clap(about = "An example CLI")]
struct Opts {
/// 输入文件
#[clap(short, long, default_value = "input.txt")]
input: String,
/// 输出文件
#[clap(short, long, default_value = "output.txt")]
output: String,
/// 详细模式
#[clap(short, long)]
verbose: bool,
/// 要处理的文件
files: Vec<String>,
}
fn main() {
let opts = Opts::parse();
println!("Options: {:?}", opts);
}工具类
uuid
uuid 用于生成 UUID。
use uuid::Uuid;
fn main() {
// 生成随机 UUID v4
let id = Uuid::new_v4();
println!("UUID v4: {}", id);
// 生成 UUID v1
let context = uuid::Context::new(0);
let id = Uuid::new_v1(
uuid::Timestamp::from_unix(&context, 1497624119, 1234),
&[1, 2, 3, 4, 5, 6],
);
// 解析 UUID
let parsed = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
println!("Parsed: {}", parsed);
// 转换为不同格式
println!("Simple: {}", id.simple());
println!("URN: {}", id.urn());
println!("Hyphenated: {}", id.hyphenated());
}chrono
chrono 是日期和时间处理库。
use chrono::{DateTime, Utc, Local, NaiveDate, Duration, TimeZone};
fn main() {
// 当前时间
let now: DateTime<Utc> = Utc::now();
println!("UTC now: {}", now);
let local: DateTime<Local> = Local::now();
println!("Local now: {}", local);
// 解析日期
let date = NaiveDate::from_ymd_opt(2024, 3, 15).unwrap();
println!("Date: {}", date);
// 时间计算
let tomorrow = now + Duration::days(1);
println!("Tomorrow: {}", tomorrow);
let two_hours_ago = now - Duration::hours(2);
println!("Two hours ago: {}", two_hours_ago);
// 格式化
let formatted = now.format("%Y-%m-%d %H:%M:%S").to_string();
println!("Formatted: {}", formatted);
// 解析字符串
let parsed = DateTime::parse_from_str(
"2024-03-15 14:30:00",
"%Y-%m-%d %H:%M:%S"
).unwrap();
println!("Parsed: {}", parsed);
// 时区转换
let utc = Utc::now();
let beijing = utc.with_timezone(&chrono::FixedOffset::east_opt(8 * 3600).unwrap());
println!("Beijing time: {}", beijing);
}regex
regex 是正则表达式库。
use regex::Regex;
fn main() {
// 编译正则表达式
let re = Regex::new(r"\d+").unwrap();
// 查找匹配
let text = "abc123def456";
for mat in re.find_iter(text) {
println!("Found: {} at {}", mat.as_str(), mat.start());
}
// 捕获组
let re = Regex::new(r"(\w+)@(\w+)\.(\w+)").unwrap();
let email = "user@example.com";
if let Some(caps) = re.captures(email) {
println!("Username: {}", &caps[1]);
println!("Domain: {}", &caps[2]);
println!("TLD: {}", &caps[3]);
}
// 替换
let result = re.replace_all("Contact: user1@example.com or user2@test.org", "$1@hidden.com");
println!("Replaced: {}", result);
// 验证邮箱格式
let email_re = Regex::new(r"^[\w.-]+@[\w.-]+\.\w+$").unwrap();
println!("Is valid: {}", email_re.is_match("test@example.com"));
}测试
tokio-test
用于测试异步代码。
#[cfg(test)]
mod tests {
use tokio_test;
use std::time::Duration;
#[tokio::test]
async fn test_async_function() {
let result = async {
tokio::time::sleep(Duration::from_millis(10)).await;
42
}.await;
assert_eq!(result, 42);
}
#[test]
fn test_async_block() {
tokio_test::block_on(async {
let result = some_async_function().await;
assert!(result.is_ok());
});
}
#[test]
#[should_panic]
fn test_timeout() {
tokio_test::block_on(async {
tokio::time::timeout(
Duration::from_millis(10),
async {
tokio::time::sleep(Duration::from_secs(1)).await;
}
).await.unwrap();
});
}
}
async fn some_async_function() -> Result<(), ()> {
Ok(())
}mockall
mockall 用于创建 Mock 对象。
use mockall::automock;
#[automock]
trait Database {
fn get_user(&self, id: u64) -> Option<User>;
fn save_user(&mut self, user: &User) -> Result<(), String>;
}
struct User {
id: u64,
name: String,
}
struct UserService<T: Database> {
db: T,
}
impl<T: Database> UserService<T> {
fn get_user_name(&self, id: u64) -> Option<String> {
self.db.get_user(id).map(|u| u.name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_user_name() {
let mut mock = MockDatabase::new();
mock.expect_get_user()
.with(mockall::predicate::eq(1))
.times(1)
.returning(|_| Some(User {
id: 1,
name: "张三".to_string(),
}));
let service = UserService { db: mock };
let name = service.get_user_name(1);
assert_eq!(name, Some("张三".to_string()));
}
}总结
本文介绍了 Rust 生态中常用的第三方库,涵盖了:
- Web 框架:Axum、Actix-web、Rocket
- 异步运行时:Tokio、async-std
- 数据库:Diesel、sqlx、redis-rs
- 序列化:Serde
- 错误处理:thiserror、anyhow
- HTTP 客户端:reqwest
- 日志:tracing、log
- CLI 工具:clap
- 工具类:uuid、chrono、regex
- 测试:tokio-test、mockall
这些 crates 都经过广泛使用和验证,可以帮助你更高效地开发 Rust 应用。建议根据项目需求选择合适的库,并始终关注 crates.io 和官方文档获取最新信息。
快乐编程,大家来 Rust! 🦀