4、Rust基础速通(二):结构体与枚举、模式匹配、错误处理(Result/Option)、常用集合(Vec/HashMap)

好,咱们接着往下走。上一章我们把 Rust 的变量、函数、所有权这些基本功过了一遍。这一章,咱们要啃几块硬骨头——结构体、枚举、模式匹配,还有错误处理和常用集合。

说实话,这些才是 Rust 真正“干活”的东西。我在做量化回测引擎的时候,每天跟它们打交道。你想想看,没有结构体你怎么组织订单数据?没有枚举你怎么表示市场状态?没有 Result 你怎么处理 API 调用失败?

嗯,咱们一个一个来。

4.1 结构体:把数据捆在一起

结构体说白了就是把你关心的数据打包成一个整体。比如一个订单,有价格、数量、方向、时间戳——你总不能每次都用四个变量传来传去吧?

struct Order {
    price: f64,
    quantity: u32,
    side: String,  // "buy" 或 "sell"
    timestamp: u64,
}

用起来也很直接:

let order = Order {
    price: 100.5,
    quantity: 1000,
    side: String::from("buy"),
    timestamp: 1699000000,
};

println!("价格: {}", order.price);
我的习惯: 在量化项目中,我通常把结构体字段按“不变字段”和“可变字段”分开。比如订单 ID 用 pub 但不可变,状态用 pub(crate)mut。这样代码一读就明白哪些是核心数据,哪些是运行时状态。

结构体还可以有方法。注意,方法是绑定到结构体上的函数,用 impl 块定义:

impl Order {
    fn is_buy(&self) -> bool {
        self.side == "buy"
    }
    
    fn value(&self) -> f64 {
        self.price * self.quantity as f64
    }
}

println!("是否买入: {}", order.is_buy());

这里有个细节:&self 是借用,不会拿走所有权。如果你要修改结构体,就用 &mut self

4.2 枚举:让状态更清晰

枚举在 Rust 里比 C 语言强大得多。它不只是列举几个值,每个变体还能携带数据。

我在做交易系统时,用枚举表示订单状态:

enum OrderStatus {
    Pending,
    PartiallyFilled { filled_qty: u32, avg_price: f64 },
    Filled { avg_price: f64 },
    Cancelled,
    Rejected(String),  // 带拒绝原因
}

你看,PartiallyFilledFilled 还带着成交数据。这在 C 语言里你得搞个联合体,一不小心就踩坑。Rust 枚举自带类型安全,编译器帮你检查。

避坑指南: 我曾经在枚举里放了大量数据,结果 match 时忘了处理某个变体——编译器直接报错。嗯,这其实是好事。Rust 的穷尽性检查(exhaustive check)逼着你把所有情况都考虑到,少了很多线上事故。

4.3 模式匹配:Rust 的瑞士军刀

模式匹配是 Rust 最爽的特性之一。配合枚举用,简直无敌。

fn handle_order(status: OrderStatus) {
    match status {
        OrderStatus::Pending => println!("等待处理"),
        OrderStatus::PartiallyFilled { filled_qty, avg_price } => {
            println!("已成交 {} 股,均价 {}", filled_qty, avg_price);
        }
        OrderStatus::Filled { avg_price } => {
            println!("全部成交,均价 {}", avg_price);
        }
        OrderStatus::Cancelled => println!("已取消"),
        OrderStatus::Rejected(reason) => println!("被拒: {}", reason),
    }
}

模式匹配还能解构结构体、元组,甚至嵌套匹配。比如:

let point = (3, 5);
match point {
    (0, 0) => println!("原点"),
    (x, 0) => println!("X 轴上,x={}", x),
    (0, y) => println!("Y 轴上,y={}", y),
    (x, y) => println!("普通点 ({}, {})", x, y),
}

你想想看,这比一堆 if-else 清晰多少?

4.4 错误处理:Result 和 Option

Rust 没有异常。它用两个枚举来处理“可能出错”和“可能为空”的情况。

Option:处理“可能有值”

比如从 HashMap 里取一个 key,可能不存在:

let mut prices = std::collections::HashMap::new();
prices.insert("AAPL", 150.0);

let price = prices.get("AAPL");  // 返回 Option<&f64>
match price {
    Some(p) => println!("AAPL 价格: {}", p),
    None => println!("找不到 AAPL"),
}

我个人习惯用 unwrap_orunwrap_or_else 给个默认值:

let price = prices.get("GOOG").copied().unwrap_or(0.0);

Result:处理“可能失败”

Result 有两个变体:Ok(T)Err(E)。比如读取文件:

use std::fs::File;
use std::io::Read;

fn read_config(path: &str) -> Result<String, std::io::Error> {
    let mut file = File::open(path)?;  // ? 运算符:出错就提前返回
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    Ok(content)
}
注意: ? 运算符只能在返回 Result 或 Option 的函数里用。我刚开始写 Rust 时老忘记这个,编译器报错才反应过来。嗯,多写几次就习惯了。

调用时:

match read_config("config.toml") {
    Ok(content) => println!("配置内容: {}", content),
    Err(e) => eprintln!("读取失败: {}", e),
}

4.5 常用集合:Vec 和 HashMap

这两个是量化开发里最常用的集合。咱们重点说。

Vec:动态数组

Vec 就是可增长的数组。存价格序列、订单列表,都用它。

let mut prices: Vec<f64> = Vec::new();
prices.push(100.5);
prices.push(101.2);
prices.push(99.8);

// 或者用宏
let mut prices = vec![100.5, 101.2, 99.8];

// 遍历
for price in &prices {
    println!("{}", price);
}

// 索引访问(注意边界检查)
if let Some(p) = prices.get(0) {
    println!("第一个价格: {}", p);
}

我在回测引擎里经常用 windows 方法做滑动窗口:

let prices = vec![100.0, 101.0, 102.0, 103.0, 104.0];
let sma: Vec<f64> = prices.windows(3)
    .map(|w| w.iter().sum::<f64>() / 3.0)
    .collect();
// sma = [101.0, 102.0, 103.0]

HashMap:键值对

存股票代码到价格的映射,或者订单 ID 到订单对象的映射,都用 HashMap。

use std::collections::HashMap;

let mut portfolio = HashMap::new();
portfolio.insert("AAPL", 100);
portfolio.insert("GOOG", 50);

// 更新
portfolio.entry("AAPL").and_modify(|e| *e += 10).or_insert(0);

// 遍历
for (ticker, shares) in &portfolio {
    println!("持有 {}: {} 股", ticker, shares);
}
性能提示: 如果你知道 HashMap 大概会存多少元素,提前用 HashMap::with_capacity(n) 分配空间,避免频繁 rehash。我在处理百万级订单簿时,这个优化能省下不少时间。

4.6 本章知识体系

下面这张图帮你理清本章的核心脉络:

Rust 基础速通(二) 结构体 组织数据 impl 方法 字段可见性 枚举 带数据变体 类型安全 穷尽性检查 模式匹配 match 表达式 解构 if let 语法 错误处理 Option<T> Result<T,E> ? 运算符 常用集合:Vec / HashMap 结构体 + 枚举 = 数据建模 模式匹配 + 错误处理 = 安全控制 Vec + HashMap = 高效存储

说白了,这一章就是 Rust 的“三板斧”:用结构体和枚举把数据组织好,用模式匹配把逻辑写清楚,用 Result/Option 把错误管住,最后用 Vec 和 HashMap 把数据存起来。量化框架里 80% 的代码都离不开这些东西。

我个人建议你多写几个小例子练手。比如写一个简单的订单簿,用 Vec 存买单和卖单,用枚举表示订单类型,用 Result 处理非法价格。写一遍比看十遍管用。

好,这一章就到这儿。记住:结构体是骨架,枚举是状态,模式匹配是逻辑,错误处理是安全网,集合是工具箱。把这几个玩熟了,Rust 的大门就真正向你敞开了。


专注资料整理