Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • 搜索未来:SEO与GEO双引擎实战手册
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • Rust 开发入门
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • 搜索未来:SEO与GEO双引擎实战手册
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • Rust 开发入门
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain

A.2 Rust常见编程模式与技巧

在深入学习 Rust 的过程中,掌握一些常见的编程模式和技巧,能够帮助你编写出更安全、高效且符合 Rust 哲学的代码。本节将介绍一些 Rust 开发者常用的模式,这些模式是 Rust 语言特性(如所有权、生命周期、Trait 等)在实际应用中的典型体现。

1. 构建者模式(Builder Pattern)

当创建一个包含多个可选参数或配置项的结构体时,构造函数会变得冗长且难以使用。构建者模式通过一个中间 Builder 结构体,将对象的构造过程分解为一系列链式调用,提高了代码的可读性和灵活性。

// 目标结构体
#[derive(Debug)]
struct Config {
    host: String,
    port: u16,
    timeout: Option<u64>,
    max_connections: u32,
}

// 构建者结构体
struct ConfigBuilder {
    host: String,
    port: u16,
    timeout: Option<u64>,
    max_connections: u32,
}

impl ConfigBuilder {
    fn new(host: &str, port: u16) -> Self {
        ConfigBuilder {
            host: host.to_string(),
            port,
            timeout: None,
            max_connections: 100, // 默认值
        }
    }

    fn timeout(mut self, timeout: u64) -> Self {
        self.timeout = Some(timeout);
        self
    }

    fn max_connections(mut self, max: u32) -> Self {
        self.max_connections = max;
        self
    }

    fn build(self) -> Result<Config, String> {
        // 可以在这里进行校验
        if self.host.is_empty() {
            return Err("Host cannot be empty".to_string());
        }
        Ok(Config {
            host: self.host,
            port: self.port,
            timeout: self.timeout,
            max_connections: self.max_connections,
        })
    }
}

fn main() {
    let config = ConfigBuilder::new("localhost", 8080)
        .timeout(30)
        .max_connections(200)
        .build()
        .expect("Failed to build config");

    println!("{:?}", config);
}

技巧:

  • Builder 的方法通常接受 self 的所有权并返回 Self,以便进行链式调用。
  • 在 build 方法中进行最终的校验和错误处理,返回 Result 类型。
  • 对于可选字段,使用 Option 类型。

2. 新类型模式(Newtype Pattern)

Rust 中的元组结构体可以用来创建“新类型”,即为一个现有类型包裹上一层语义。这可以让你在不增加运行时开销的情况下,获得类型安全和更强的表达能力。

// 使用新类型来表示不同的单位
struct Meters(f64);
struct Seconds(f64);

fn speed(distance: Meters, time: Seconds) -> f64 {
    distance.0 / time.0
}

fn main() {
    let distance = Meters(100.0);
    let time = Seconds(9.58);

    // 下面这行代码编译错误,因为类型不匹配
    // let invalid = speed(time, distance);

    let s = speed(distance, time);
    println!("Speed: {}", s);
}

技巧:

  • 新类型可以让你为基本类型添加领域特定的含义,避免混淆。
  • 可以通过实现 Deref、From、Into 等 Trait 来方便地与新类型内部的值进行交互。
  • 常用于封装外部库的类型,以提供更安全的接口。

3. 类型状态模式(Type State Pattern)

类型状态模式利用 Rust 强大的类型系统,在编译时强制对象只能处于合法的状态,并只能执行该状态下允许的操作。这可以有效地将运行时错误转化为编译时错误。

// 定义不同的状态
struct Open;
struct Closed;

// 门结构体,使用泛型参数表示状态
struct Door<State> {
    is_locked: bool,
    _state: std::marker::PhantomData<State>,
}

impl Door<Closed> {
    fn new() -> Self {
        Door {
            is_locked: true,
            _state: std::marker::PhantomData,
        }
    }

    fn open(self) -> Door<Open> {
        println!("Door is now open.");
        Door {
            is_locked: self.is_locked,
            _state: std::marker::PhantomData,
        }
    }
}

impl Door<Open> {
    fn close(self) -> Door<Closed> {
        println!("Door is now closed.");
        Door {
            is_locked: self.is_locked,
            _state: std::marker::PhantomData,
        }
    }

    fn knock(&self) {
        println!("Knock knock!");
    }
}

fn main() {
    let closed_door = Door::<Closed>::new();
    // closed_door.knock(); // 编译错误!Closed 状态没有 knock 方法

    let open_door = closed_door.open();
    open_door.knock(); // 可以敲门
    // open_door.open(); // 编译错误!Open 状态没有 open 方法

    let _closed_door_again = open_door.close();
}

技巧:

  • 使用零大小的类型(如空结构体)来表示状态。
  • 使用 PhantomData 来“消费”状态类型参数,而不实际存储它。
  • 每个状态实现不同的方法集,从而在编译时保证操作的安全性。

4. 访问者模式(Visitor Pattern)

Rust 的迭代器(Iterator)和 serde 等库大量使用了访问者模式。通过定义一个 Visitor Trait,可以将算法与数据结构分离,使得可以在不修改数据结构的情况下添加新的操作。

// 一个简单的表达式树
#[derive(Debug)]
enum Expr {
    Num(i32),
    Add(Box<Expr>, Box<Expr>),
    Mul(Box<Expr>, Box<Expr>),
}

// 访问者 Trait
trait Visitor {
    fn visit_num(&mut self, n: i32);
    fn visit_add(&mut self);
    fn visit_mul(&mut self);
}

// 一个具体的访问者:计算表达式的值
struct Evaluator {
    result: i32,
}

impl Evaluator {
    fn new() -> Self {
        Evaluator { result: 0 }
    }
}

impl Visitor for Evaluator {
    fn visit_num(&mut self, n: i32) {
        self.result = n;
    }

    fn visit_add(&mut self) {
        // 这里简化处理,实际需要从栈中获取左右操作数
        println!("Evaluating add");
    }

    fn visit_mul(&mut self) {
        println!("Evaluating mul");
    }
}

// 数据结构接受访问者的方法
impl Expr {
    fn accept(&self, visitor: &mut dyn Visitor) {
        match self {
            Expr::Num(n) => visitor.visit_num(*n),
            Expr::Add(left, right) => {
                left.accept(visitor);
                right.accept(visitor);
                visitor.visit_add();
            }
            Expr::Mul(left, right) => {
                left.accept(visitor);
                right.accept(visitor);
                visitor.visit_mul();
            }
        }
    }
}

fn main() {
    let expr = Expr::Add(
        Box::new(Expr::Num(1)),
        Box::new(Expr::Mul(Box::new(Expr::Num(2)), Box::new(Expr::Num(3)))),
    );

    let mut evaluator = Evaluator::new();
    expr.accept(&mut evaluator);
}

技巧:

  • 访问者模式非常适合遍历复杂的数据结构,如 AST(抽象语法树)。
  • 结合 Box 和 dyn Trait 可以实现动态分发。
  • Iterator 和 serde::Deserialize 是 Rust 标准库和生态中应用此模式的绝佳例子。

5. 使用 ? 运算符进行错误传播

这是 Rust 中最常用也最重要的一个技巧。? 运算符可以极大地简化错误处理代码,将 Result 或 Option 的展开和提前返回合并为一个简洁的表达式。

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    // 传统写法
    // let mut file = match File::open("username.txt") {
    //     Ok(file) => file,
    //     Err(e) => return Err(e),
    // };

    // 使用 ? 运算符
    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_chain() -> Result<String, io::Error> {
    let mut username = String::new();
    File::open("username.txt")?.read_to_string(&mut username)?;
    Ok(username)
}

fn main() {
    match read_username_from_file_chain() {
        Ok(name) => println!("Username: {}", name),
        Err(e) => eprintln!("Error reading file: {}", e),
    }
}

技巧:

  • ? 运算符只能用于返回 Result 或 Option 的函数中。
  • 它会在遇到 Err 或 None 时,将错误或 None 值返回给调用者。
  • 可以结合 map_err 进行错误类型的转换。

掌握这些模式与技巧,将帮助你写出更地道、更健壮的 Rust 代码。它们是 Rust 社区智慧的结晶,也是从 Rust 新手迈向熟练工的重要一步。

Last Updated:: 5/9/26, 3:13 PM