第五篇:Rust 进阶指南:泛型、Trait、生命周期、错误处理、集合

10 阅读23分钟

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();

集合性能对比

操作VecHashMapHashSetBTreeMapBTreeSet
插入(末尾)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 代码至关重要!