Rust 进阶指南:泛型、Trait、生命周期、错误处理、集合
全面深入讲解 Rust 的核心高级特性。
目录
泛型 (Generics)
泛型允许编写适用于多种类型的代码,类似于 Java/C++ 的模板。
基础概念
为什么需要泛型?
// ❌ 没有泛型:需要为每种类型写重复代码
fn print_i32(value: i32) {
println!("{}", value);
}
fn print_string(value: String) {
println!("{}", value);
}
// ✅ 使用泛型:一个函数处理多种类型
fn print<T: std::fmt::Display>(value: T) {
println!("{}", value);
}
fn main() {
print(42); // T = i32
print("hello"); // T = &str
print(3.14); // T = f64
}
泛型函数
基本语法
// T 是类型参数
fn largest<T>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest { // 需要 T 实现 PartialOrd
largest = item;
}
}
largest
}
// 使用
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
let result = largest(&numbers);
println!("最大值: {}", result);
}
多个类型参数
fn pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
fn main() {
let p1 = pair(1, "hello"); // T=i32, U=&str
let p2 = pair(3.14, true); // T=f64, U=bool
}
带约束的泛型
use std::fmt::Display;
// T 必须实现 Display trait
fn print_and_return<T: Display>(value: T) -> T {
println!("Value: {}", value);
value
}
// 多个约束
fn compare_and_print<T: Display + PartialOrd>(a: T, b: T) {
if a > b {
println!("{} > {}", a, b);
} else {
println!("{} <= {}", a, b);
}
}
// where 子句(更清晰)
fn complex_function<T, U>(a: T, b: U) -> String
where
T: Display + Clone,
U: Display + PartialEq,
{
format!("a: {}, b: {}", a, b)
}
泛型结构体
基本结构体
// 单个类型参数
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn new(x: T, y: T) -> Self {
Point { x, y }
}
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let integer_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };
}
多个类型参数
struct Point<T, U> {
x: T,
y: U,
}
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p1 = Point { x: 5, y: 10.4 }; // T=i32, U=f64
let p2 = Point { x: "Hello", y: 'c' }; // T=&str, U=char
let p3 = p1.mixup(p2); // Point<i32, char>
println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}
为特定类型实现方法
impl Point<f64> {
// 只有 Point<f64> 有这个方法
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p = Point { x: 3.0, y: 4.0 };
println!("距离: {}", p.distance_from_origin()); // 可以调用
let p2 = Point { x: 3, y: 4 };
// p2.distance_from_origin(); // ❌ 编译错误:i32 版本没有此方法
}
泛型枚举
Option
// 标准库定义
enum Option<T> {
Some(T),
None,
}
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None
} else {
Some(a / b)
}
}
fn main() {
match divide(10.0, 2.0) {
Some(result) => println!("结果: {}", result),
None => println!("除数为零"),
}
}
Result<T, E>
// 标准库定义
enum Result<T, E> {
Ok(T),
Err(E),
}
fn parse_number(s: &str) -> Result<i32, String> {
match s.parse() {
Ok(n) => Ok(n),
Err(_) => Err(format!("无法解析: {}", s)),
}
}
本项目示例
缓存泛型实现
// src/utils/cache.rs 的简化版本
use serde::{Serialize, Deserialize};
pub struct RedisCache {
client: redis::Client,
}
impl RedisCache {
// 泛型 get 方法
pub async fn get<T>(&self, key: &str) -> Result<Option<T>, redis::RedisError>
where
T: for<'de> Deserialize<'de>, // T 必须可反序列化
{
let mut conn = self.client.get_async_connection().await?;
let value: Option<String> = redis::cmd("GET")
.arg(key)
.query_async(&mut conn)
.await?;
match value {
Some(json) => {
let data = serde_json::from_str(&json).ok();
Ok(data)
}
None => Ok(None),
}
}
// 泛型 set 方法
pub async fn set<T>(&self, key: &str, value: &T, ttl: usize) -> Result<(), redis::RedisError>
where
T: Serialize, // T 必须可序列化
{
let json = serde_json::to_string(value).unwrap();
let mut conn = self.client.get_async_connection().await?;
redis::cmd("SETEX")
.arg(key)
.arg(ttl)
.arg(json)
.query_async(&mut conn)
.await
}
}
// 使用示例
async fn example(cache: &RedisCache) {
// 存储不同类型
cache.set("user:1", &User { id: 1, name: "Alice" }, 3600).await;
cache.set("count", &42i32, 3600).await;
// 取出不同类型
let user: Option<User> = cache.get("user:1").await.unwrap();
let count: Option<i32> = cache.get("count").await.unwrap();
}
响应包装器
#[derive(Serialize)]
struct ApiResponse<T> {
success: bool,
data: Option<T>,
error: Option<String>,
}
impl<T> ApiResponse<T> {
fn ok(data: T) -> Self {
ApiResponse {
success: true,
data: Some(data),
error: None,
}
}
fn err(message: String) -> Self {
ApiResponse {
success: false,
data: None,
error: Some(message),
}
}
}
// 使用
async fn get_user_handler() -> Json<ApiResponse<UserResponse>> {
match get_user(1).await {
Ok(user) => Json(ApiResponse::ok(user)),
Err(e) => Json(ApiResponse::err(e.to_string())),
}
}
泛型性能
Rust 使用单态化(Monomorphization):
fn generic_function<T>(value: T) -> T {
value
}
fn main() {
generic_function(5); // 编译器生成 generic_function_i32
generic_function("hi"); // 编译器生成 generic_function_str
}
// 编译后相当于:
fn generic_function_i32(value: i32) -> i32 { value }
fn generic_function_str(value: &str) -> &str { value }
优点: 零运行时开销
缺点: 编译时间增加,二进制文件变大
Trait
Trait 定义类型的行为,类似于接口(Interface)。
基础概念
定义和实现 Trait
// 定义 trait
trait Summary {
fn summarize(&self) -> String;
}
// 为类型实现 trait
struct Article {
title: String,
content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}: {}", self.title, self.content)
}
}
struct Tweet {
username: String,
content: String,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("@{}: {}", self.username, self.content)
}
}
fn main() {
let article = Article {
title: "Rust".to_string(),
content: "Great!".to_string(),
};
let tweet = Tweet {
username: "rustlang".to_string(),
content: "Rust 1.70 released!".to_string(),
};
println!("{}", article.summarize());
println!("{}", tweet.summarize());
}
默认实现
trait Summary {
fn summarize_author(&self) -> String;
// 默认实现
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
struct Tweet {
username: String,
content: String,
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
// 可以使用默认的 summarize,也可以覆盖
}
fn main() {
let tweet = Tweet {
username: "rustlang".to_string(),
content: "Hello".to_string(),
};
println!("{}", tweet.summarize()); // 使用默认实现
}
Trait 作为参数
impl Trait 语法
fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
// 等价于 trait bound 语法
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
fn main() {
let article = Article { /* ... */ };
notify(&article); // 任何实现 Summary 的类型都可以
}
多个 Trait 约束
use std::fmt::Display;
fn notify(item: &(impl Summary + Display)) {
println!("{}", item);
println!("{}", item.summarize());
}
// 或使用 where 子句
fn notify<T>(item: &T)
where
T: Summary + Display,
{
println!("{}", item);
println!("{}", item.summarize());
}
多个参数
// 两个参数可以是不同类型
fn notify(item1: &impl Summary, item2: &impl Summary) {
// item1 和 item2 可以是不同的类型(只要都实现 Summary)
}
// 强制相同类型
fn notify<T: Summary>(item1: &T, item2: &T) {
// item1 和 item2 必须是相同类型
}
Trait 作为返回值
impl Trait 返回
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("horse_ebooks"),
content: String::from("of course"),
}
}
// ⚠️ 注意:只能返回一种具体类型
fn returns_different_types(switch: bool) -> impl Summary {
if switch {
Article { /* ... */ } // ❌ 编译错误!
} else {
Tweet { /* ... */ }
}
}
Trait 对象(动态分发)
// 使用 Box<dyn Trait> 返回不同类型
fn returns_summarizable(switch: bool) -> Box<dyn Summary> {
if switch {
Box::new(Article {
title: String::from("Title"),
content: String::from("Content"),
})
} else {
Box::new(Tweet {
username: String::from("user"),
content: String::from("tweet"),
})
}
}
常用标准 Trait
Debug
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 1, y: 2 };
println!("{:?}", p); // Point { x: 1, y: 2 }
println!("{:#?}", p); // 美化输出
}
Clone
#[derive(Clone)]
struct Person {
name: String,
age: u32,
}
fn main() {
let person1 = Person {
name: String::from("Alice"),
age: 30,
};
let person2 = person1.clone(); // 深拷贝
}
Copy
// Copy 类型在赋值时会自动拷贝,而不是移动
#[derive(Copy, Clone)] // Copy 需要 Clone
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // 拷贝,而不是移动
println!("{:?} {:?}", p1, p2); // p1 仍然有效
}
// ⚠️ 注意:包含 String、Vec 等堆分配类型不能 derive Copy
PartialEq 和 Eq
#[derive(PartialEq)]
struct Person {
name: String,
age: u32,
}
fn main() {
let p1 = Person { name: "Alice".to_string(), age: 30 };
let p2 = Person { name: "Alice".to_string(), age: 30 };
assert_eq!(p1, p2); // 比较所有字段
}
PartialOrd 和 Ord
#[derive(PartialEq, PartialOrd)]
struct Person {
name: String,
age: u32,
}
fn main() {
let p1 = Person { name: "Alice".to_string(), age: 30 };
let p2 = Person { name: "Bob".to_string(), age: 25 };
if p1 > p2 {
println!("p1 > p2");
}
}
Display
use std::fmt;
struct Person {
name: String,
age: u32,
}
impl fmt::Display for Person {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ({}岁)", self.name, self.age)
}
}
fn main() {
let person = Person {
name: "Alice".to_string(),
age: 30,
};
println!("{}", person); // Alice (30岁)
}
From 和 Into
struct Kilometers(i32);
impl From<i32> for Kilometers {
fn from(value: i32) -> Self {
Kilometers(value)
}
}
fn main() {
let km = Kilometers::from(10);
let km: Kilometers = 10.into(); // 自动获得 Into
}
Default
#[derive(Default)]
struct Config {
host: String, // 默认 ""
port: u16, // 默认 0
debug: bool, // 默认 false
}
fn main() {
let config = Config::default();
}
// 自定义 Default
impl Default for Config {
fn default() -> Self {
Config {
host: "localhost".to_string(),
port: 8080,
debug: false,
}
}
}
本项目示例
From Trait 用于类型转换
// src/models/user.rs
impl From<User> for UserResponse {
fn from(user: User) -> Self {
UserResponse {
id: user.id,
username: user.username,
email: user.email,
created_at: user.created_at,
updated_at: user.updated_at,
}
}
}
// 使用
fn handler() -> Json<UserResponse> {
let user: User = get_user_from_db();
let response: UserResponse = user.into(); // 使用 From/Into
Json(response)
}
Serialize 和 Deserialize
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct User {
id: i64,
username: String,
email: String,
}
// 自动获得 JSON 序列化能力
fn example() {
let user = User { /* ... */ };
// 序列化
let json = serde_json::to_string(&user).unwrap();
// 反序列化
let user: User = serde_json::from_str(&json).unwrap();
}
SQLx FromRow
use sqlx::FromRow;
#[derive(FromRow)]
struct User {
id: i64,
username: String,
email: String,
}
// 自动从数据库行转换
async fn get_user(pool: &MySqlPool, id: i64) -> Result<User, sqlx::Error> {
let user = sqlx::query_as::<_, User>(
"SELECT id, username, email FROM users WHERE id = ?"
)
.bind(id)
.fetch_one(pool)
.await?;
Ok(user)
}
Trait 对象和动态分发
静态分发 vs 动态分发
trait Draw {
fn draw(&self);
}
struct Circle;
struct Square;
impl Draw for Circle {
fn draw(&self) {
println!("Drawing circle");
}
}
impl Draw for Square {
fn draw(&self) {
println!("Drawing square");
}
}
// 静态分发:编译时确定类型
fn draw_static<T: Draw>(shape: &T) {
shape.draw();
}
// 动态分发:运行时确定类型
fn draw_dynamic(shape: &dyn Draw) {
shape.draw();
}
fn main() {
let circle = Circle;
let square = Square;
// 静态分发
draw_static(&circle);
draw_static(&square);
// 动态分发
draw_dynamic(&circle);
draw_dynamic(&square);
// 存储不同类型(需要动态分发)
let shapes: Vec<Box<dyn Draw>> = vec![
Box::new(Circle),
Box::new(Square),
];
for shape in shapes {
shape.draw();
}
}
Trait 对象限制
// ⚠️ 不是所有 trait 都可以作为 trait 对象
trait NotObjectSafe {
fn new() -> Self; // 返回 Self
fn generic<T>(&self, value: T); // 泛型方法
}
// ✅ Object-safe trait
trait ObjectSafe {
fn method(&self);
}
关联类型
trait Iterator {
type Item; // 关联类型
fn next(&mut self) -> Option<Self::Item>;
}
struct Counter {
count: u32,
}
impl Iterator for Counter {
type Item = u32; // 指定具体类型
fn next(&mut self) -> Option<u32> {
self.count += 1;
Some(self.count)
}
}
fn main() {
let mut counter = Counter { count: 0 };
println!("{:?}", counter.next()); // Some(1)
}
关联类型 vs 泛型
// 使用泛型:可以为同一类型实现多次
trait Add<Rhs> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
impl Add<i32> for Point {
type Output = Point;
fn add(self, rhs: i32) -> Point { /* ... */ }
}
impl Add<Point> for Point {
type Output = Point;
fn add(self, rhs: Point) -> Point { /* ... */ }
}
// 使用关联类型:只能实现一次
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
高级 Trait
Supertraits
// Display 是 ToString 的 supertrait
trait ToString: Display {
fn to_string(&self) -> String {
format!("{}", self)
}
}
// 实现 ToString 必须先实现 Display
newtype 模式
// 绕过孤儿规则
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
生命周期 (Lifetimes)
生命周期确保引用的有效性,防止悬垂指针。
为什么需要生命周期?
// ❌ 悬垂引用
fn dangling_reference() -> &String {
let s = String::from("hello");
&s // ❌ s 在函数结束时被销毁
}
// ✅ 返回所有权
fn valid_reference() -> String {
let s = String::from("hello");
s // 移动所有权
}
生命周期语法
基本语法
// 'a 是生命周期参数
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string");
let string2 = String::from("short");
let result = longest(string1.as_str(), string2.as_str());
println!("最长: {}", result);
}
生命周期含义
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
// 返回值的生命周期与 x 和 y 中较短的那个相同
}
fn main() {
let string1 = String::from("long");
{
let string2 = String::from("short");
let result = longest(&string1, &string2);
println!("{}", result); // ✅ 正确:result 在 string2 作用域内
}
// println!("{}", result); // ❌ 错误:result 已失效
}
生命周期省略规则
编译器可以自动推断某些情况的生命周期:
规则 1:每个引用参数都有独立的生命周期
// 显式
fn foo<'a, 'b>(x: &'a i32, y: &'b i32) { }
// 省略后
fn foo(x: &i32, y: &i32) { }
规则 2:只有一个输入生命周期,赋给所有输出
// 显式
fn foo<'a>(x: &'a i32) -> &'a i32 { x }
// 省略后
fn foo(x: &i32) -> &i32 { x }
规则 3:多个输入,如果有 &self 或 &mut self,其生命周期赋给所有输出
impl<'a> MyStruct<'a> {
// 显式
fn method<'b>(&'a self, x: &'b i32) -> &'a i32 { &self.value }
// 省略后
fn method(&self, x: &i32) -> &i32 { &self.value }
}
结构体中的生命周期
// 结构体包含引用,必须标注生命周期
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().unwrap();
let excerpt = ImportantExcerpt {
part: first_sentence,
};
println!("{}", excerpt.part);
}
多个生命周期
struct Context<'a, 'b> {
first: &'a str,
second: &'b str,
}
fn main() {
let first = String::from("first");
{
let second = String::from("second");
let ctx = Context {
first: &first,
second: &second,
};
}
}
方法中的生命周期
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
// 根据省略规则,返回值生命周期来自 &self
fn level(&self) -> i32 {
3
}
// 返回引用需要标注生命周期
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("注意: {}", announcement);
self.part
}
}
静态生命周期
// 'static 表示整个程序运行期间
let s: &'static str = "I have a static lifetime.";
// 字符串字面量自动拥有 'static 生命周期
fn get_string() -> &'static str {
"static string"
}
// ⚠️ 不要滥用 'static
// 只有真正需要整个程序期间存活的引用才用 'static
生命周期约束
// T 必须比 'a 活得长
fn longest_with_constraint<'a, T>(x: &'a T, y: &'a T) -> &'a T
where
T: 'a + std::fmt::Display,
{
if format!("{}", x).len() > format!("{}", y).len() {
x
} else {
y
}
}
本项目示例
数据库查询
// SQLx 自动处理生命周期
async fn get_user_by_username<'a>(
pool: &MySqlPool,
username: &'a str,
) -> Result<Option<User>, sqlx::Error> {
let user = sqlx::query_as::<_, User>(
"SELECT * FROM users WHERE username = ?"
)
.bind(username) // username 的生命周期由 SQLx 管理
.fetch_optional(pool)
.await?;
Ok(user)
}
控制器中的引用
use axum::extract::State;
use std::sync::Arc;
// Arc 让我们避免处理复杂的生命周期
pub async fn get_user(
State(state): State<Arc<AppState>>, // Arc 让多个请求共享状态
Path(id): Path<i64>,
) -> Result<Json<UserResponse>, StatusCode> {
let user = services::user_service::get_user_by_id(
&state.pool,
&state.cache,
id,
)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
match user {
Some(u) => Ok(Json(u.into())),
None => Err(StatusCode::NOT_FOUND),
}
}
生命周期常见问题
问题 1:借用时间过长
// ❌ 错误
fn example() {
let mut data = vec![1, 2, 3];
let first = &data[0]; // 不可变借用
data.push(4); // ❌ 错误:可变借用
println!("{}", first);
}
// ✅ 修复:缩短借用作用域
fn example() {
let mut data = vec![1, 2, 3];
{
let first = &data[0];
println!("{}", first);
} // first 借用结束
data.push(4); // ✅ 正确
}
问题 2:返回引用
// ❌ 错误:返回局部变量的引用
fn get_string() -> &str {
let s = String::from("hello");
&s // ❌ s 被销毁
}
// ✅ 修复1:返回所有权
fn get_string() -> String {
String::from("hello")
}
// ✅ 修复2:使用 'static
fn get_string() -> &'static str {
"hello" // 字符串字面量
}
// ✅ 修复3:接受一个引用并返回
fn get_first<'a>(s: &'a str) -> &'a str {
&s[0..1]
}
问题 3:结构体自引用
// ❌ 错误:自引用结构体
struct SelfReferential<'a> {
data: String,
reference: &'a str, // 引用 data
}
// ✅ 解决方案:使用 Pin 和 unsafe(高级)
// 或避免自引用设计
错误处理
Rust 使用 Result 和 Option 进行错误处理,没有异常。
Option
基本用法
fn find_user(id: i64) -> Option<User> {
if id == 1 {
Some(User { /* ... */ })
} else {
None
}
}
fn main() {
// 方式1:match
match find_user(1) {
Some(user) => println!("找到用户: {:?}", user),
None => println!("用户不存在"),
}
// 方式2:if let
if let Some(user) = find_user(1) {
println!("找到用户: {:?}", user);
}
// 方式3:unwrap(慎用,会 panic)
let user = find_user(1).unwrap();
// 方式4:unwrap_or(提供默认值)
let user = find_user(1).unwrap_or(default_user);
// 方式5:unwrap_or_else(惰性默认值)
let user = find_user(1).unwrap_or_else(|| create_default_user());
}
Option 方法
let some_num = Some(10);
let none_num: Option<i32> = None;
// is_some / is_none
assert!(some_num.is_some());
assert!(none_num.is_none());
// map:转换内部值
let doubled = some_num.map(|x| x * 2); // Some(20)
// and_then:链式操作
let result = some_num
.and_then(|x| Some(x * 2))
.and_then(|x| Some(x + 1)); // Some(21)
// or:提供备选
let result = none_num.or(Some(0)); // Some(0)
// filter:条件过滤
let even = some_num.filter(|x| x % 2 == 0); // Some(10)
// take:取出值并留下 None
let mut option = Some(5);
let value = option.take(); // value = Some(5), option = None
Result<T, E>
基本用法
use std::fs::File;
use std::io::{self, Read};
fn read_file(path: &str) -> Result<String, io::Error> {
let mut file = File::open(path)?; // ? 传播错误
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
fn main() {
// 方式1:match
match read_file("hello.txt") {
Ok(contents) => println!("文件内容: {}", contents),
Err(e) => eprintln!("错误: {}", e),
}
// 方式2:if let
if let Ok(contents) = read_file("hello.txt") {
println!("文件内容: {}", contents);
}
// 方式3:unwrap
let contents = read_file("hello.txt").unwrap();
// 方式4:expect(带自定义消息)
let contents = read_file("hello.txt").expect("无法读取文件");
// 方式5:unwrap_or
let contents = read_file("hello.txt").unwrap_or_default();
}
? 操作符
// ? 等价于:
match result {
Ok(value) => value,
Err(e) => return Err(e.into()), // 自动转换错误类型
}
// 示例
fn read_username_from_file() -> Result<String, io::Error> {
let mut file = File::open("username.txt")?; // 如果失败,返回错误
let mut username = String::new();
file.read_to_string(&mut username)?; // 如果失败,返回错误
Ok(username) // 成功
}
// 链式调用
fn read_username_from_file() -> Result<String, io::Error> {
let mut username = String::new();
File::open("username.txt")?.read_to_string(&mut username)?;
Ok(username)
}
// 更简洁
fn read_username_from_file() -> Result<String, io::Error> {
std::fs::read_to_string("username.txt")
}
Result 方法
let ok_result: Result<i32, &str> = Ok(10);
let err_result: Result<i32, &str> = Err("error");
// is_ok / is_err
assert!(ok_result.is_ok());
assert!(err_result.is_err());
// map:转换成功值
let doubled = ok_result.map(|x| x * 2); // Ok(20)
// map_err:转换错误值
let mapped = err_result.map_err(|e| format!("Error: {}", e));
// and_then:链式操作
let result = ok_result
.and_then(|x| Ok(x * 2))
.and_then(|x| Ok(x + 1)); // Ok(21)
// or:提供备选
let result = err_result.or(Ok(0)); // Ok(0)
// unwrap_or / unwrap_or_else
let value = err_result.unwrap_or(0); // 0
let value = err_result.unwrap_or_else(|_| 0); // 0
自定义错误类型
使用枚举
#[derive(Debug)]
enum MyError {
IoError(std::io::Error),
ParseError(std::num::ParseIntError),
CustomError(String),
}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
MyError::IoError(e) => write!(f, "IO 错误: {}", e),
MyError::ParseError(e) => write!(f, "解析错误: {}", e),
MyError::CustomError(s) => write!(f, "自定义错误: {}", s),
}
}
}
impl std::error::Error for MyError {}
// 使用
fn do_something() -> Result<(), MyError> {
let file = std::fs::read_to_string("file.txt")
.map_err(MyError::IoError)?;
let num: i32 = file.trim().parse()
.map_err(MyError::ParseError)?;
if num < 0 {
return Err(MyError::CustomError("负数不允许".to_string()));
}
Ok(())
}
使用 thiserror
use thiserror::Error;
#[derive(Error, Debug)]
enum MyError {
#[error("IO 错误: {0}")]
IoError(#[from] std::io::Error),
#[error("解析错误: {0}")]
ParseError(#[from] std::num::ParseIntError),
#[error("自定义错误: {0}")]
CustomError(String),
}
// 使用 #[from] 自动实现 From trait
fn do_something() -> Result<(), MyError> {
let file = std::fs::read_to_string("file.txt")?; // 自动转换
let num: i32 = file.trim().parse()?; // 自动转换
if num < 0 {
return Err(MyError::CustomError("负数不允许".to_string()));
}
Ok(())
}
使用 anyhow
use anyhow::{Context, Result};
fn do_something() -> Result<()> {
let file = std::fs::read_to_string("file.txt")
.context("无法读取配置文件")?;
let num: i32 = file.trim().parse()
.context("无法解析数字")?;
if num < 0 {
anyhow::bail!("数字不能为负");
}
Ok(())
}
fn main() {
if let Err(e) = do_something() {
eprintln!("错误: {:?}", e);
// 打印错误链
for cause in e.chain() {
eprintln!(" caused by: {}", cause);
}
}
}
本项目错误处理
SQLx 错误处理
// src/services/user_service.rs
pub async fn get_user_by_id(
pool: &MySqlPool,
cache: &RedisCache,
id: i64,
) -> Result<Option<User>, sqlx::Error> {
// 缓存层错误被忽略(使用 let _ 或 .ok())
let cache_key = format!("user:{}", id);
if let Ok(Some(cached)) = cache.get::<User>(&cache_key).await {
return Ok(Some(cached));
}
// 数据库错误传播(使用 ?)
let user = sqlx::query_as::<_, User>(
"SELECT id, username, email, password_hash, created_at, updated_at
FROM users WHERE id = ?"
)
.bind(id)
.fetch_optional(pool)
.await?; // 传播 sqlx::Error
// 缓存写入错误被忽略
if let Some(ref u) = user {
let _ = cache.set(&cache_key, u, 3600).await;
}
Ok(user)
}
控制器错误映射
// src/controllers/user_controller.rs
pub async fn get_user(
State(state): State<Arc<AppState>>,
Path(id): Path<i64>,
) -> Result<Json<UserResponse>, StatusCode> {
// 将服务层错误映射为 HTTP 状态码
let user = services::user_service::get_user_by_id(
&state.pool,
&state.cache,
id,
)
.await
.map_err(|e| {
eprintln!("数据库错误: {}", e);
StatusCode::INTERNAL_SERVER_ERROR
})?;
// 处理 Option
match user {
Some(u) => Ok(Json(u.into())),
None => Err(StatusCode::NOT_FOUND),
}
}
主函数错误处理
// src/main.rs
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 使用 anyhow::Result 简化错误处理
let config = Config::from_env()
.context("无法加载配置")?;
let pool = database::create_pool(&config.database_url).await
.context("无法连接数据库")?;
database::init_database(&pool).await
.context("无法初始化数据库")?;
let cache = RedisCache::new(&config.redis_url).await
.context("无法连接 Redis")?;
// 启动服务器
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.context("服务器错误")?;
Ok(())
}
错误处理最佳实践
1. 库代码使用自定义错误
// 库代码
pub enum MyLibError {
InvalidInput,
DatabaseError,
}
pub fn library_function() -> Result<(), MyLibError> {
// ...
}
2. 应用代码使用 anyhow
// 应用代码
use anyhow::Result;
fn application_function() -> Result<()> {
library_function()?;
Ok(())
}
3. 不要过度使用 unwrap
// ❌ 生产代码中避免
let value = some_function().unwrap();
// ✅ 使用 ? 或 match
let value = some_function()?;
// ✅ 或提供默认值
let value = some_function().unwrap_or_default();
4. 使用 expect 提供上下文
// ❌ 不好
let config = load_config().unwrap();
// ✅ 更好
let config = load_config()
.expect("配置文件必须存在且格式正确");
5. 错误要有意义
// ❌ 不好
return Err("error".to_string());
// ✅ 更好
return Err(format!("无法找到用户 ID: {}", user_id));
集合 (Collections)
Rust 标准库提供的动态数据结构。
Vec - 动态数组
创建
// 方式1:new
let mut v1: Vec<i32> = Vec::new();
// 方式2:vec! 宏
let v2 = vec![1, 2, 3, 4, 5];
// 方式3:with_capacity(预分配容量)
let mut v3 = Vec::with_capacity(10);
// 方式4:from
let v4 = Vec::from([1, 2, 3]);
// 方式5:collect
let v5: Vec<i32> = (1..=5).collect();
添加元素
let mut v = Vec::new();
v.push(1); // 添加到末尾
v.push(2);
v.push(3);
v.insert(1, 10); // 在索引 1 插入 10
// v = [1, 10, 2, 3]
v.extend([4, 5, 6]); // 添加多个元素
// v = [1, 10, 2, 3, 4, 5, 6]
访问元素
let v = vec![1, 2, 3, 4, 5];
// 方式1:索引(可能 panic)
let third = v[2]; // 3
// 方式2:get(返回 Option)
match v.get(2) {
Some(&num) => println!("第三个元素: {}", num),
None => println!("没有第三个元素"),
}
// 方式3:first / last
let first = v.first(); // Some(&1)
let last = v.last(); // Some(&5)
修改元素
let mut v = vec![1, 2, 3];
// 通过索引修改
v[0] = 10;
// 通过 get_mut
if let Some(elem) = v.get_mut(1) {
*elem = 20;
}
// v = [10, 20, 3]
删除元素
let mut v = vec![1, 2, 3, 4, 5];
v.pop(); // 删除并返回最后一个:Some(5)
v.remove(1); // 删除索引 1 的元素:2
v.swap_remove(0); // 快速删除(用最后元素替换)
v.clear(); // 清空所有元素
遍历
let v = vec![1, 2, 3];
// 不可变遍历
for num in &v {
println!("{}", num);
}
// 可变遍历
let mut v = vec![1, 2, 3];
for num in &mut v {
*num += 1;
}
// 获取所有权
for num in v {
println!("{}", num);
}
// v 已经被移动,不能再使用
常用方法
let mut v = vec![3, 1, 4, 1, 5];
v.len(); // 长度:5
v.is_empty(); // 是否为空:false
v.capacity(); // 容量
v.reserve(10); // 预留容量
v.contains(&3); // 是否包含:true
v.sort(); // 排序:[1, 1, 3, 4, 5]
v.reverse(); // 反转
v.dedup(); // 去除连续重复:[5, 4, 3, 1]
// 切片
let slice = &v[1..3]; // [4, 3]
// 分割
let (left, right) = v.split_at(2); // ([5, 4], [3, 1])
// 迭代器方法
let doubled: Vec<_> = v.iter().map(|x| x * 2).collect();
let sum: i32 = v.iter().sum();
let filtered: Vec<_> = v.iter().filter(|&&x| x > 2).collect();
本项目示例
// src/services/user_service.rs
pub async fn list_users(
pool: &MySqlPool,
) -> Result<Vec<User>, sqlx::Error> {
let users = sqlx::query_as::<_, User>(
"SELECT id, username, email, password_hash, created_at, updated_at
FROM users
ORDER BY id DESC
LIMIT 100"
)
.fetch_all(pool) // 返回 Vec<User>
.await?;
Ok(users)
}
// 控制器
pub async fn list_users(
State(state): State<Arc<AppState>>,
) -> Result<Json<Vec<UserResponse>>, StatusCode> {
let users = services::user_service::list_users(&state.pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 转换 Vec<User> 到 Vec<UserResponse>
let response: Vec<UserResponse> = users
.into_iter()
.map(|user| user.into())
.collect();
Ok(Json(response))
}
String 和 &str
创建 String
// 方式1:from
let s1 = String::from("hello");
// 方式2:to_string
let s2 = "hello".to_string();
// 方式3:new
let mut s3 = String::new();
// 方式4:with_capacity
let s4 = String::with_capacity(10);
String vs &str
// String:可变、堆分配、拥有所有权
let mut s = String::from("hello");
s.push_str(" world");
// &str:不可变、可能在栈或堆、借用
let s: &str = "hello"; // 字符串字面量
let s: &str = &my_string[..]; // 字符串切片
修改 String
let mut s = String::from("hello");
s.push_str(" world"); // 追加字符串
s.push('!'); // 追加字符
s.insert(5, ','); // 插入字符
s.insert_str(6, " my"); // 插入字符串
// 拼接
let s1 = String::from("Hello");
let s2 = String::from("World");
let s3 = s1 + " " + &s2; // s1 被移动
// format! 宏(不获取所有权)
let s1 = String::from("Hello");
let s2 = String::from("World");
let s3 = format!("{} {}", s1, s2); // s1 和 s2 仍可用
字符串操作
let s = String::from("Hello, 世界!");
s.len(); // 字节长度:13
s.chars().count(); // 字符数量:9
s.is_empty(); // 是否为空
// 大小写
s.to_uppercase(); // "HELLO, 世界!"
s.to_lowercase(); // "hello, 世界!"
// 裁剪
s.trim(); // 去除首尾空白
s.trim_start();
s.trim_end();
// 替换
s.replace("Hello", "Hi"); // "Hi, 世界!"
s.replacen("l", "L", 2); // "HeLLo, 世界!"
// 分割
let parts: Vec<&str> = s.split(',').collect(); // ["Hello", " 世界!"]
let lines: Vec<&str> = s.lines().collect();
// 判断
s.starts_with("Hello"); // true
s.ends_with("!"); // true
s.contains("世"); // true
字符串切片
let s = String::from("hello");
// ⚠️ 必须按 UTF-8 字符边界切片
let slice = &s[0..2]; // "he"
// ❌ 错误:中文字符占 3 字节
let s = String::from("你好");
// let slice = &s[0..1]; // panic!
// ✅ 正确:使用 chars
let first_char = s.chars().next();
本项目示例
// src/services/user_service.rs
// 简单的密码哈希(生产环境应使用 bcrypt)
pub async fn create_user(
pool: &MySqlPool,
req: CreateUserRequest,
) -> Result<User, sqlx::Error> {
let password_hash = format!("hashed_{}", req.password); // 拼接字符串
let result = sqlx::query(
"INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)"
)
.bind(&req.username) // 绑定 String
.bind(&req.email)
.bind(&password_hash)
.execute(pool)
.await?;
// ...
}
// 缓存键生成
let cache_key = format!("user:{}", id); // 字符串格式化
HashMap<K, V>
创建
use std::collections::HashMap;
// 方式1:new
let mut map: HashMap<String, i32> = HashMap::new();
// 方式2:from
let map = HashMap::from([
("one", 1),
("two", 2),
("three", 3),
]);
// 方式3:collect
let keys = vec!["one", "two", "three"];
let values = vec![1, 2, 3];
let map: HashMap<_, _> = keys.into_iter().zip(values).collect();
插入和访问
let mut scores = HashMap::new();
// 插入
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// 访问
let team_name = String::from("Blue");
let score = scores.get(&team_name); // Some(&10)
// 或使用 copied
let score = scores.get(&team_name).copied(); // Some(10)
// 或使用索引(可能 panic)
let score = scores[&team_name]; // 10
更新
let mut scores = HashMap::new();
// 覆盖
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25); // 覆盖旧值
// 只在键不存在时插入
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50); // 不会插入
// 基于旧值更新
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
// {"hello": 1, "world": 2, "wonderful": 1}
删除
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.remove("Blue"); // 删除并返回 Some(10)
scores.remove("Red"); // 返回 None
遍历
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// 遍历键值对
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// 只遍历键
for key in scores.keys() {
println!("{}", key);
}
// 只遍历值
for value in scores.values() {
println!("{}", value);
}
// 可变遍历值
for value in scores.values_mut() {
*value += 10;
}
常用方法
let mut scores = HashMap::new();
scores.insert("Blue", 10);
scores.insert("Yellow", 50);
scores.len(); // 长度:2
scores.is_empty(); // 是否为空:false
scores.contains_key("Blue"); // 是否包含键:true
scores.clear(); // 清空
// entry API
scores.entry("Blue").and_modify(|v| *v += 10).or_insert(50);
示例:统计单词
use std::collections::HashMap;
fn word_count(text: &str) -> HashMap<String, usize> {
let mut counts = HashMap::new();
for word in text.split_whitespace() {
let word = word.to_lowercase();
*counts.entry(word).or_insert(0) += 1;
}
counts
}
fn main() {
let text = "Hello world hello Rust world";
let counts = word_count(text);
for (word, count) in &counts {
println!("{}: {}", word, count);
}
}
HashSet
创建
use std::collections::HashSet;
let mut set: HashSet<i32> = HashSet::new();
// from
let set = HashSet::from([1, 2, 3, 4, 5]);
// collect
let set: HashSet<_> = vec![1, 2, 3].into_iter().collect();
基本操作
let mut set = HashSet::new();
// 插入
set.insert(1);
set.insert(2);
set.insert(2); // 重复元素不会被添加
// 删除
set.remove(&1);
// 检查
set.contains(&2); // true
set.len(); // 1
set.is_empty(); // false
// 遍历
for value in &set {
println!("{}", value);
}
集合运算
let set1: HashSet<_> = vec![1, 2, 3, 4].into_iter().collect();
let set2: HashSet<_> = vec![3, 4, 5, 6].into_iter().collect();
// 并集
let union: HashSet<_> = set1.union(&set2).collect();
// {1, 2, 3, 4, 5, 6}
// 交集
let intersection: HashSet<_> = set1.intersection(&set2).collect();
// {3, 4}
// 差集
let difference: HashSet<_> = set1.difference(&set2).collect();
// {1, 2}
// 对称差集
let symmetric_difference: HashSet<_> = set1.symmetric_difference(&set2).collect();
// {1, 2, 5, 6}
// 子集判断
set1.is_subset(&set2); // false
set1.is_superset(&set2); // false
set1.is_disjoint(&set2); // false(有交集)
示例:去重
use std::collections::HashSet;
fn remove_duplicates(vec: Vec<i32>) -> Vec<i32> {
let set: HashSet<_> = vec.into_iter().collect();
set.into_iter().collect()
}
fn main() {
let numbers = vec![1, 2, 2, 3, 3, 3, 4, 5, 5];
let unique = remove_duplicates(numbers);
println!("{:?}", unique); // [1, 2, 3, 4, 5](顺序可能不同)
}
BTreeMap 和 BTreeSet
有序版本的 HashMap 和 HashSet。
use std::collections::{BTreeMap, BTreeSet};
// BTreeMap:键按顺序排列
let mut map = BTreeMap::new();
map.insert(3, "c");
map.insert(1, "a");
map.insert(2, "b");
for (k, v) in &map {
println!("{}: {}", k, v); // 按键排序:1, 2, 3
}
// BTreeSet:元素按顺序排列
let mut set = BTreeSet::new();
set.insert(3);
set.insert(1);
set.insert(2);
for v in &set {
println!("{}", v); // 1, 2, 3
}
VecDeque - 双端队列
use std::collections::VecDeque;
let mut deque = VecDeque::new();
// 从前面添加/删除
deque.push_front(1);
deque.push_front(2);
let front = deque.pop_front(); // Some(2)
// 从后面添加/删除
deque.push_back(3);
deque.push_back(4);
let back = deque.pop_back(); // Some(4)
// deque = [1, 3]
迭代器常用操作
let numbers = vec![1, 2, 3, 4, 5];
// map:转换
let doubled: Vec<_> = numbers.iter().map(|x| x * 2).collect();
// filter:过滤
let evens: Vec<_> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
// filter_map:过滤并转换
let result: Vec<_> = vec!["1", "two", "3"]
.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect(); // [1, 3]
// fold:累积
let sum = numbers.iter().fold(0, |acc, x| acc + x); // 15
// find:查找第一个匹配
let first_even = numbers.iter().find(|&&x| x % 2 == 0); // Some(&2)
// any / all
let has_even = numbers.iter().any(|&x| x % 2 == 0); // true
let all_positive = numbers.iter().all(|&x| x > 0); // true
// take / skip
let first_three: Vec<_> = numbers.iter().take(3).collect(); // [1, 2, 3]
let skip_two: Vec<_> = numbers.iter().skip(2).collect(); // [3, 4, 5]
// zip:组合
let names = vec!["Alice", "Bob", "Carol"];
let ages = vec![30, 25, 35];
let pairs: Vec<_> = names.iter().zip(ages.iter()).collect();
// [("Alice", 30), ("Bob", 25), ("Carol", 35)]
// enumerate:带索引
for (i, value) in numbers.iter().enumerate() {
println!("{}: {}", i, value);
}
// chain:连接
let combined: Vec<_> = vec![1, 2].iter().chain(vec![3, 4].iter()).collect();
集合性能对比
| 操作 | Vec | HashMap | HashSet | BTreeMap | BTreeSet |
|---|---|---|---|---|---|
| 插入(末尾) | O(1) | O(1) | O(1) | O(log n) | O(log n) |
| 插入(开头) | O(n) | - | - | - | - |
| 查找 | O(n) | O(1) | O(1) | O(log n) | O(log n) |
| 删除 | O(n) | O(1) | O(1) | O(log n) | O(log n) |
| 有序 | 插入顺序 | 无序 | 无序 | 有序 | 有序 |
本项目集合使用示例
// 批量查询用户
pub async fn get_users_by_ids(
pool: &MySqlPool,
ids: Vec<i64>,
) -> Result<Vec<User>, sqlx::Error> {
// 使用 HashSet 去重
let unique_ids: HashSet<_> = ids.into_iter().collect();
// 构建 IN 查询
let placeholders = (0..unique_ids.len())
.map(|_| "?")
.collect::<Vec<_>>()
.join(",");
let query = format!(
"SELECT * FROM users WHERE id IN ({})",
placeholders
);
let mut query = sqlx::query_as::<_, User>(&query);
for id in unique_ids {
query = query.bind(id);
}
let users = query.fetch_all(pool).await?;
Ok(users)
}
// 用户名到 ID 的映射
pub fn build_username_map(users: Vec<User>) -> HashMap<String, i64> {
users
.into_iter()
.map(|user| (user.username, user.id))
.collect()
}
总结
泛型
- 编写适用于多种类型的代码
- 使用
<T>语法 - 通过 trait bounds 约束类型
- 零运行时开销(单态化)
Trait
- 定义类型的行为(类似接口)
- 使用
impl Trait for Type - 常用 derive:Debug, Clone, Serialize 等
- Trait 对象支持动态分发
生命周期
- 确保引用有效性
- 使用
'a语法标注 - 编译器自动推断简单情况
- 避免悬垂引用
错误处理
- 使用
Option<T>处理可能不存在的值 - 使用
Result<T, E>处理可能失败的操作 ?操作符简化错误传播- anyhow/thiserror 简化自定义错误
集合
Vec<T>:动态数组String:可变字符串HashMap<K, V>:键值对HashSet<T>:唯一值集合- 迭代器提供强大的函数式操作
这些特性是 Rust 的核心,掌握它们对编写高质量 Rust 代码至关重要!