🦀 Rust 结构体与枚举

结构体组织数据,枚举表达选择,Trait 定义行为——三者结合,就是 Rust 类型系统的表达力核心。掌握它们,你才能写出编译器帮你检查一切的代码。

structenumpattern matchinggenericstraitdyn Traitnewtype

📑 目录

  1. 结构体 — 数据的骨架
  2. 方法 — impl 块与关联函数
  3. 枚举 — 选择的类型
  4. 模式匹配进阶 — 解构的艺术
  5. 泛型 — 参数化的类型
  6. Trait — 行为的契约
  7. 常用 Trait 速查
  8. Trait Object — 动态分发
  9. 高级类型 — newtype、别名与 DST
  10. 实战:用 enum + trait 实现表达式求值器

1. 结构体 — 数据的骨架

结构体是 Rust 中组织命名字段的主要方式。与 C 结构体类似但更严格——没有未初始化字段,没有继承,取而代之的是组合和 trait。

1.1 三种结构体

rust
// 1. 命名字段结构体 — 最常用
struct User {
    name: String,
    email: String,
    active: bool,
    sign_in_count: u64,
}

// 2. 元组结构体 — 字段有类型但没名字
struct Color(u8, u8, u8);
struct Point(f64, f64, f64);

// 3. 单元结构体 — 零大小,用于标记
struct AlwaysEqual;

⚠️ 字段默认私有:即使结构体本身是 pub,字段仍默认私有。要让外部访问,需要逐个标注 pub。这是 Rust 的"最小可见性"哲学。

1.2 字段简写与结构体更新语法

rust
fn build_user(name: String, email: String) -> User {
    User {
        name,           // 字段简写:变量名与字段名相同
        email,
        active: true,
        sign_in_count: 1,
    }
}

fn update_email(user: User, new_email: String) -> User {
    User {
        email: new_email,
        ..user          // 其余字段从 user 复制/移动
    }
    // 注意:user 中非 Copy 字段被移动后,user 不可用
}

🚫 ..语法的所有权陷阱..user 不是"复制所有字段",而是逐字段移动。如果任何字段没有 Copy,它就从 user 移走,user 就不再可用。

1.3 元组结构体与解构

rust
struct Color(u8, u8, u8);
let red = Color(255, 0, 0);
let r = red.0;              // 索引访问
let Color(r, g, b) = red;  // 解构

// 元组结构体的价值:类型安全
fn paint(c: Color) { /* ... */ }
// paint((255, 0, 0));       // ❌ 类型不匹配
// paint(Color(255, 0, 0));  // ✅

1.4 单元结构体

rust
struct Marker;
use std::mem::size_of;
assert_eq!(size_of::<Marker>(), 0);  // 零大小类型

// 用途 1:trait 实现标记
trait Visitor { fn visit(&self); }
impl Visitor for Marker {
    fn visit(&self) { println!("default visit"); }
}

// 用途 2:类型级状态机
struct Unconfigured;
struct Configured;
struct Connection<S> { state: S, addr: String }
// Connection<Unconfigured> 和 Connection<Configured> 是不同类型!

1.5 Debug 打印

rust
#[derive(Debug)]
struct Point { x: f64, y: f64 }

let p = Point { x: 1.0, y: 2.0 };
println!("{:?}", p);   // Point { x: 1.0, y: 2.0 }
println!("{:#?}", p);  // 美化输出

// 自定义 Debug
use std::fmt;
impl fmt::Debug for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

2. 方法 — impl 块与关联函数

Rust 没有 class,方法通过 impl 块绑定到类型上。数据和行为分开定义,但保持关联。

2.1 方法的三种 self

rust
#[derive(Debug)]
struct Counter { value: i32 }

impl Counter {
    // &self — 只读借用
    fn value(&self) -> i32 { self.value }

    // &mut self — 可变借用
    fn increment(&mut self) -> &mut Self {
        self.value += 1;
        self
    }

    // self — 消耗所有权
    fn into_value(self) -> i32 { self.value }
}
self 形式所有权典型场景
&self只读借用getter、计算、显示
&mut self可变借用修改内部状态
self消耗析构/转换,之后 self 不可用

2.2 关联函数(构造器模式)

rust
impl Counter {
    // 关联函数:没有 self,类似"静态方法"
    fn new() -> Self { Self { value: 0 } }
    fn with_value(v: i32) -> Self { Self { value: v } }
}

let c1 = Counter::new();           // 用 :: 调用
let c2 = Counter::with_value(42);

💡 Self 大写Self 是当前实现类型的别名,self 是实例。在 impl CounterSelf = Counter。写 Self 而非 Counter,方便后续重构。

2.3 链式调用

rust
impl Counter {
    fn increment(&mut self) -> &mut Self { self.value += 1; self }
    fn add(&mut self, n: i32) -> &mut Self { self.value += n; self }
}

let mut c = Counter::new();
c.increment().add(10).increment();
assert_eq!(c.value(), 12);

2.4 多个 impl 块

rust
// Rust 允许对同一类型写多个 impl 块
impl Counter {
    fn new() -> Self { Self { value: 0 } }
}
impl Counter {
    fn value(&self) -> i32 { self.value }
}

// 常见用法:不同泛型约束分不同 impl 块
impl<T: Clone> Container<T> {
    fn clone_first(&self) -> Option<T> { /* ... */ }
}
impl<T: Display> Container<T> {
    fn print_first(&self) { /* ... */ }
}

3. 枚举 — 选择的类型

Rust 的枚举远比 C/Java 强大——每个变体可以携带不同类型和数量的数据。枚举 + match 是 Rust 代替继承和多态的核心工具。

3.1 携带数据的变体

rust
enum Message {
    Quit,                        // 无数据
    Move { x: i32, y: i32 },    // 命名字段
    Write(String),               // 包含 String
    ChangeColor(u8, u8, u8),     // 三个 u8
}

let msgs = vec![
    Message::Quit,
    Message::Move { x: 10, y: 20 },
    Message::Write(String::from("hello")),
    Message::ChangeColor(255, 0, 0),
];

for msg in msgs {
    match msg {
        Message::Quit => println!("Quit"),
        Message::Move { x, y } => println!("Move to ({}, {})", x, y),
        Message::Write(text) => println!("Message: {}", text),
        Message::ChangeColor(r, g, b) => println!("Color: #{r:02x}{g:02x}{b:02x}"),
    }
}

✅ 枚举 vs 继承:在 OOP 语言中你可能用基类+子类表达不同消息。在 Rust 中 enum + match 更简洁、更安全,编译器保证你处理了所有情况。

3.2 Option vs null

rust
// Rust 没有 null,用 Option 表达"可能没有值"
enum Option<T> { Some(T), None }

fn find_user(id: u32) -> Option<String> {
    if id == 1 { Some(String::from("Alice")) }
    else { None }
}

// 必须显式处理 None
match find_user(1) {
    Some(name) => println!("Found: {}", name),
    None       => println!("Not found"),
}

// 常用快捷方法
let name = find_user(1).unwrap_or("unknown".to_string());
let name = find_user(1).unwrap_or_default();
let name = find_user(1).ok_or("user not found")?;  // 转 Result

✅ Rust Option

fn get(&self, key: &str) -> Option<&Value>

// 编译器强制处理 None
match map.get("foo") {
    Some(v) => use(v),
    None    => handle_missing(),
}

❌ Null / 忘记检查

// Java: NullPointerException 运行时爆炸
Value v = map.get("foo");
v.doSomething(); // 💥 if null

// Go: 忘记检查 error
v, _ := map.Get("foo")
v.DoSomething() // 💥 if nil

3.3 match 穷举

rust
enum Color { Red, Green, Blue }

// ❌ 编译错误:缺少 Blue
// match c {
//     Color::Red => "red",
//     Color::Green => "green",
// }

// ✅ 必须穷举,或用 _ 兜底
fn describe(c: Color) -> &'static str {
    match c {
        Color::Red   => "red",
        Color::Green => "green",
        Color::Blue  => "blue",
    }
}

// if let — 只关心一种情况
if let Some(name) = find_user(1) {
    println!("Found: {}", name);
}

4. 模式匹配进阶 — 解构的艺术

match 只是最常见的场景。let 绑定、函数参数、for 循环都可以解构。

4.1 解构结构体

rust
struct Point { x: i32, y: i32 }
let p = Point { x: 10, y: 20 };

let Point { x, y } = p;      // let 解构
let Point { x, .. } = p;     // 只取部分

match p {
    Point { x: 0, y: 0 } => println!("origin"),
    Point { x: 0, y }    => println!("on y-axis, y={}", y),
    Point { x, y: 0 }    => println!("on x-axis, x={}", x),
    Point { x, y }        => println!("({}, {})", x, y),
}

4.2 解构枚举与元组

rust
enum Shape {
    Circle { radius: f64 },
    Rectangle { width: f64, height: f64 },
}

fn area(s: &Shape) -> f64 {
    match s {
        Shape::Circle { radius } => std::f64::consts::PI * radius * radius,
        Shape::Rectangle { width, height } => width * height,
    }
}

// 元组解构
match (1, -2, 3) {
    (0, y, z) => println!("first=0, y={}, z={}", y, z),
    (1, ..)   => println!("first=1, rest ignored"),
    _         => println!("other"),
}

// 函数参数解构
fn print_coords((x, y): (i32, i32)) {
    println!("({}, {})", x, y);
}

4.3 @ 绑定

rust
match age {
    n @ 0..=12   => println!("child, age={}", n),
    n @ 13..=19  => println!("teen, age={}", n),
    n @ 20..=150 => println!("adult, age={}", n),
    _            => println!("invalid"),
}

4.4 守卫条件

rust
match (x, y) {
    (a, b) if a == b => println!("equal"),
    (a, b) if a > b  => println!("a > b"),
    _                => println!("a < b"),
}

// 守卫与 @ 结合
match score {
    s @ 90..=100 if s % 10 == 0 => println!("round A: {}", s),
    s @ 80..=89                 => println!("B: {}", s),
    _                           => println!("C or below"),
}

⚠️ 守卫条件绕过穷举检查:编译器不会分析 if 条件是否覆盖所有情况,所以使用守卫时通常需要 _ 兜底。

5. 泛型 — 参数化的类型

泛型让你写一次代码适用于多种类型。Rust 的泛型通过单态化(monomorphization)在编译时展开,运行时零成本。

5.1 函数泛型

rust
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in &list[1..] {
        if item > largest { largest = item; }
    }
    largest
}

let nums = vec![1, 5, 3, 9, 2];
let chars = vec!['y', 'a', 'm'];
assert_eq!(*largest(&nums), 9);
assert_eq!(*largest(&chars), 'y');

5.2 结构体与枚举泛型

rust
struct Point<T> { x: T, y: T }

let integer = Point { x: 5, y: 10 };
let float   = Point { x: 1.0, y: 4.0 };
// let mix = Point { x: 5, y: 4.0 };  // ❌ 类型不匹配

// 多类型参数
struct Point2<T, U> { x: T, y: U }
let mix = Point2 { x: 5, y: 4.0 };  // ✅

// 标准库中最常用的泛型枚举
enum Option<T> { Some(T), None }
enum Result<T, E> { Ok(T), Err(E) }

// 自定义:二叉树
enum BinaryTree<T> {
    Leaf(T),
    Node { left: Box<BinaryTree<T>>, value: T, right: Box<BinaryTree<T>> },
}

5.3 方法中的泛型

rust
impl<T> Point<T> {
    fn x(&self) -> &T { &self.x }
}

// 只为 f64 实现 distance_from_origin
impl Point<f64> {
    fn distance_from_origin(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

let p: Point<f64> = Point { x: 3.0, y: 4.0 };
assert_eq!(p.distance_from_origin(), 5.0);

// let p: Point<i32> = Point { x: 3, y: 4 };
// p.distance_from_origin();  // ❌ i32 版本没这方法

💡 单态化 = 零成本Point<i32>Point<f64> 是完全不同的类型,编译器为每种使用到的类型参数生成一份专门代码。没有虚函数表,没有运行时查找。

6. Trait — 行为的契约

Trait 定义类型能做什么,impl ... for ... 提供具体实现。Trait 是 Rust 实现多态和代码复用的核心。

6.1 定义与实现

rust
trait Summary {
    fn summarize(&self) -> String;
}

struct Article { title: String, content: String }
struct Tweet { username: String, content: String }

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}: {}...", self.title, &self.content[..50.min(self.content.len())])
    }
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("@{}: {}", self.username, self.content)
    }
}

6.2 默认实现

rust
trait Summary {
    fn summarize_author(&self) -> String;

    // 默认实现可以调用 trait 中的其他方法
    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

impl Summary for Article {
    fn summarize_author(&self) -> String { self.title.clone() }
    // summarize() 使用默认实现
}

6.3 Trait 作为参数

rust
// 方式 1:impl Trait(静态分发)
fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

// 方式 2:Trait bound(等价写法,更灵活)
fn notify<T: Summary>(item: &T) { /* ... */ }

// 多 trait bound
fn notify<T: Summary + Display>(item: &T) { /* ... */ }

// where 子句(更清晰)
fn some_fn<T, U>(t: &T, u: &U) -> i32
where T: Display + Clone, U: Clone + Debug { /* ... */ }

6.4 返回 impl Trait vs Box<dyn Trait>

rust
// 返回 impl Trait:隐藏具体类型,但只能返回一种类型
fn create() -> impl Summary {
    Article { title: "Rust".into(), content: "...".into() }
}

// ❌ 不能条件返回不同类型
// fn create(flag: bool) -> impl Summary {
//     if flag { Article { ... } } else { Tweet { ... } }  // 编译错误!
// }

// ✅ 用 Box<dyn Trait> 动态分发
fn create(flag: bool) -> Box<dyn Summary> {
    if flag {
        Box::new(Article { title: "Rust".into(), content: "...".into() })
    } else {
        Box::new(Tweet { username: "alice".into(), content: "hi".into() })
    }
}

6.5 Trait bound 与条件实现

rust
struct Pair<T> { x: T, y: T }

impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self { Self { x, y } }
}

// 只在 T 实现了 Display + PartialOrd 时才有此方法
impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y { println!("x >= y: {}", self.x); }
        else { println!("x < y: {}", self.x); }
    }
}

// blanket impl:为所有实现了某 trait 的类型自动实现另一 trait
impl<T: Summary> Display for T {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.summarize())
    }
}

7. 常用 Trait 速查

Trait作用derive关键方法
Debug调试格式化 {:?}✅ 常用fmt(&self, f)
Display用户友好格式化 {}❌ 手动fmt(&self, f)
Clone显式深拷贝✅ 常用clone(&self) -> Self
Copy隐式复制(位拷贝)✅ 前提:Clone标记 trait
PartialEq相等比较 ==✅ 常用eq(&self, other)
Eq全等关系✅ 前提:PartialEq标记 trait
Hash哈希值计算✅ 常用hash(&self, state)
From<T>从 T 转换❌ 手动from(T) -> Self
Into<T>转换为 T🤖 自动into(self) -> T
Iterator迭代器❌ 手动next(&mut self)
Default默认值✅ 常用default() -> Self

7.1 Debug vs Display

✅ Debug:开发者视角

#[derive(Debug)]
struct User { name: String, age: u32 }

println!("{:?}", user);
// User { name: "Alice", age: 30 }
println!("{:#?}", user);
// 美化多行输出

✅ Display:用户视角

impl fmt::Display for User {
    fn fmt(&self, f: &mut fmt::Formatter)
        -> fmt::Result
    {
        write!(f, "{} (age {})",
            self.name, self.age)
    }
}
println!("{}", user);
// Alice (age 30)

7.2 Clone vs Copy

✅ Clone:显式深拷贝

let s1 = String::from("hello");
let s2 = s1.clone();  // 显式调用
// s1 仍然可用

❌ Copy vs 移动

let x = 42;
let y = x;  // i32: Copy,隐式复制

let s1 = String::from("hello");
let s2 = s1;  // String: 移动!
// s1 不可用了

⚠️ Copy 的前提:所有字段都必须是 Copy 的。String 不是 Copy(持有堆内存),所以含 String 字段的结构体也不能 Copy

7.3 From / Into 转换

rust
struct UserId(i32);

impl From<i32> for UserId {
    fn from(id: i32) -> Self { UserId(id) }
}

let id1 = UserId::from(42);
let id2: UserId = 42.into();  // Into 自动获得(blanket impl)

// 常见用法:错误类型转换配合 ? 运算符
impl From<io::Error> for AppError {
    fn from(e: io::Error) -> Self { AppError::Io(e) }
}
// ? 自动调用 From::from 进行转换

7.4 Iterator

rust
struct Counter { count: u32, max: u32 }

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        if self.count < self.max { self.count += 1; Some(self.count) }
        else { None }
    }
}

let sum: u32 = Counter { count: 0, max: 5 }.sum();  // 1+2+3+4+5 = 15

8. Trait Object — 动态分发

impl Trait 是静态分发(编译时确定类型),dyn Trait 是动态分发(运行时查虚函数表)。两者互补,各有所长。

8.1 静态 vs 动态分发

✅ 静态分发(impl Trait / 泛型)

fn process(v: &impl Summary) {
    v.summarize();
}
// 编译器为每种类型生成一份代码
// 零运行时开销
// 二进制可能更大(单态化)

✅ 动态分发(dyn Trait)

fn process(v: &dyn Summary) {
    v.summarize();
}
// 通过 vtable 运行时间接调用
// 微小运行时开销
// 二进制更小

8.2 Box<dyn Trait> — 异构集合

rust
trait Draw { fn draw(&self); }

struct Screen {
    components: Vec<Box<dyn Draw>>,  // 异构集合
}

impl Screen {
    fn run(&self) {
        for c in &self.components { c.draw(); }
    }
}

struct Button { label: String }
impl Draw for Button {
    fn draw(&self) { println!("Button: {}", self.label); }
}

struct TextField { text: String }
impl Draw for TextField {
    fn draw(&self) { println!("TextField: {}", self.text); }
}

let screen = Screen {
    components: vec![
        Box::new(Button { label: "OK".into() }),
        Box::new(TextField { text: "Enter name".into() }),
    ],
};
screen.run();

8.3 对象安全

rust
// ❌ 不对象安全:返回 Self
trait Factory {
    fn create() -> Self;  // Self 在 trait object 中未知
}

// ❌ 不对象安全:泛型方法
trait Bad {
    fn convert<T>(&self, t: T) -> T;  // 泛型 = 无限个方法
}

// ✅ 对象安全的 trait
trait Safe {
    fn do_thing(&self) -> i32;       // &self,返回具体类型
}

🚫 对象安全规则:方法不能返回 Self、不能有泛型参数、不能使用 Self 作为参数。需要 dyn 时提前检查。

8.4 Sized 与 ?Sized

rust
// 默认泛型参数隐式有 Sized 约束
fn foo<T>(t: T) {}  // 等价于 fn foo<T: Sized>(t: T)

// ?Sized 放宽约束,允许不确定大小的类型
fn bar<T: ?Sized>(t: &T) {}  // &T 是胖指针(ptr + len/metadata)

// dyn Trait 本身是 ?Sized 的
let s: &dyn Summary = &article;  // 胖指针:数据指针 + vtable

9. 高级类型 — newtype、别名与 DST

9.1 Newtype 模式

rust
// 为现有类型创建新的独立类型
struct Meters(u32);
struct Kilometers(u32);

fn add_meters(a: Meters, b: Meters) -> Meters {
    Meters(a.0 + b.0)
}

// add_meters(Meters(1), Kilometers(1));  // ❌ 类型安全!

// newtype 绕过孤儿规则:为外部类型实现外部 trait
use std::fmt;
struct Wrapper(Vec<String>);

impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

9.2 类型别名

rust
type Kilometers = i32;  // 不是新类型,只是别名

let x: i32 = 5;
let y: Kilometers = 5;
let z = x + y;  // ✅ 编译通过!Kilometers 就是 i32

// 常见用法:简化复杂类型
type Thunk = Box<dyn Fn() + Send + 'static>;
type Result<T> = std::result::Result<T, MyError>;

✅ Newtype

struct Meters(u32);
// Meters ≠ u32(类型安全)
// 需要手动实现所有 trait
// 可以添加额外方法

✅ Type Alias

type Meters = u32;
// Meters = u32(只是别名)
// 继承原类型所有实现
// 零开销,但无类型安全

9.3 Never 类型 (!)

rust
// ! 类型:永远不返回的函数的返回类型
fn diverge() -> ! {
    panic!("this function never returns");
}

// ! 可以强制转换为任何类型
let x: i32 = match option {
    Some(v) => v,
    None => diverge(),  // ! → i32
};

// continue 也有 ! 类型
loop {
    let val = match try_parse(input) {
        Some(v) => v,
        None => continue,  // ! → 任何类型
    };
}

9.4 动态大小类型 (DST)

rust
// DST:编译期不知道大小的类型
// - str(不是 &str!)
// - [T](不是 &[T]!)
// - dyn Trait

// DST 只能通过指针使用
let s: &str = "hello";      // &str = 胖指针(ptr + len)
let a: &[i32] = &[1, 2, 3];    // &[i32] = 胖指针(ptr + len)
let t: &dyn Draw = &button;   // &dyn Draw = 胖指针(ptr + vtable)

// Box 也能持有 DST
let boxed: Box<str> = "hello".into();  // 堆上 str,胖指针

💡 胖指针:普通引用 &i32 是一个指针大小。胖指针 &str / &[T] / &dyn Trait 是两个指针大小——一个指向数据,一个存元数据(长度或 vtable)。

10. 实战:用 enum + trait 实现表达式求值器

这个实战综合运用了 struct、enum、trait、泛型、模式匹配——构建一个支持变量和函数的 AST 求值器。

10.1 AST 定义

rust
use std::collections::HashMap;

/// 表达式 AST — 用 enum 递归定义树结构
#[derive(Debug, Clone)]
enum Expr {
    Number(f64),
    Variable(String),
    BinaryOp {
        op: BinOp,
        left: Box<Expr>,
        right: Box<Expr>,
    },
    UnaryOp {
        op: UnOp,
        operand: Box<Expr>,
    },
    Call {
        name: String,
        args: Vec<Expr>,
    },
    Cond {
        condition: Box<Expr>,
        then_branch: Box<Expr>,
        else_branch: Box<Expr>,
    },
}

#[derive(Debug, Clone, Copy)]
enum BinOp { Add, Sub, Mul, Div, Mod, Pow }

#[derive(Debug, Clone, Copy)]
enum UnOp { Neg, Abs }

10.2 求值环境(trait 设计)

rust
/// 环境接口 — 解耦变量/函数存储
trait Environment {
    fn get_var(&self, name: &str) -> Option<f64>;
    fn set_var(&mut self, name: &str, value: f64);
    fn call_fn(&self, name: &str, args: &[f64]) -> Option<f64>;
}

/// 基础环境实现
#[derive(Debug, Default)]
struct BasicEnv {
    vars: HashMap<String, f64>,
    fns: HashMap<String, Box<dyn Fn(&[f64]) -> Option<f64>>>,
}

impl BasicEnv {
    fn new() -> Self {
        let mut env = Self::default();
        // 内置函数
        env.fns.insert("sin".into(), Box::new(|args| {
            args.first().map(|&x| x.sin())
        }));
        env.fns.insert("cos".into(), Box::new(|args| {
            args.first().map(|&x| x.cos())
        }));
        env.fns.insert("max".into(), Box::new(|args| {
            match args {
                [a, b, ..] => Some(a.max(*b)),
                _ => None,
            }
        }));
        env.fns.insert("sqrt".into(), Box::new(|args| {
            args.first().map(|&x| x.sqrt())
        }));
        env
    }
}

impl Environment for BasicEnv {
    fn get_var(&self, name: &str) -> Option<f64> {
        self.vars.get(name).copied()
    }

    fn set_var(&mut self, name: &str, value: f64) {
        self.vars.insert(name.to_string(), value);
    }

    fn call_fn(&self, name: &str, args: &[f64]) -> Option<f64> {
        self.fns.get(name)?.(args)
    }
}

10.3 求值器核心

rust
#[derive(Debug, Clone)]
enum EvalError {
    UndefinedVariable(String),
    UndefinedFunction(String),
    DivisionByZero,
    ArityMismatch { name: String, expected: usize, got: usize },
    InvalidOperation(String),
}

impl Expr {
    fn eval(&self, env: &dyn Environment) -> Result<f64, EvalError> {
        match self {
            Expr::Number(n) => Ok(*n),

            Expr::Variable(name) => {
                env.get_var(name)
                    .ok_or_else(|| EvalError::UndefinedVariable(name.clone()))
            }

            Expr::BinaryOp { op, left, right } => {
                let l = left.eval(env)?;
                let r = right.eval(env)?;
                match op {
                    BinOp::Add => Ok(l + r),
                    BinOp::Sub => Ok(l - r),
                    BinOp::Mul => Ok(l * r),
                    BinOp::Div => {
                        if r == 0.0 { Err(EvalError::DivisionByZero) }
                        else { Ok(l / r) }
                    }
                    BinOp::Mod => Ok(l % r),
                    BinOp::Pow => Ok(l.powf(r)),
                }
            }

            Expr::UnaryOp { op, operand } => {
                let v = operand.eval(env)?;
                match op {
                    UnOp::Neg => Ok(-v),
                    UnOp::Abs => Ok(v.abs()),
                }
            }

            Expr::Call { name, args } => {
                let evaluated: Vec<f64> = args.iter()
                    .map(|a| a.eval(env))
                    .collect::<Result<_, _>>()?;
                env.call_fn(name, &evaluated)
                    .ok_or_else(|| EvalError::UndefinedFunction(name.clone()))
            }

            Expr::Cond { condition, then_branch, else_branch } => {
                let cond = condition.eval(env)?;
                if cond != 0.0 {
                    then_branch.eval(env)
                } else {
                    else_branch.eval(env)
                }
            }
        }
    }
}

10.4 使用示例

rust
fn main() {
    let mut env = BasicEnv::new();
    env.set_var("x", 3.0);
    env.set_var("pi", std::f64::consts::PI);

    // 数学表达式:sin(pi / 2) + x * 2
    let expr = Expr::BinaryOp {
        op: BinOp::Add,
        left: Box::new(Expr::Call {
            name: "sin".into(),
            args: vec![Expr::BinaryOp {
                op: BinOp::Div,
                left: Box::new(Expr::Variable("pi".into())),
                right: Box::new(Expr::Number(2.0)),
            }],
        }),
        right: Box::new(Expr::BinaryOp {
            op: BinOp::Mul,
            left: Box::new(Expr::Variable("x".into())),
            right: Box::new(Expr::Number(2.0)),
        }),
    };

    match expr.eval(&env) {
        Ok(result) => println!("Result: {:.4}", result),
        Err(e) => println!("Error: {:?}", e),
    }
    // Result: 7.0000  (sin(π/2) = 1.0, plus 3.0 * 2.0 = 6.0)

    // 条件表达式:if x > 2 then x * x else 0
    let cond_expr = Expr::Cond {
        condition: Box::new(Expr::BinaryOp {
            op: BinOp::Sub,
            left: Box::new(Expr::Variable("x".into())),
            right: Box::new(Expr::Number(2.0)),
        }),
        then_branch: Box::new(Expr::BinaryOp {
            op: BinOp::Mul,
            left: Box::new(Expr::Variable("x".into())),
            right: Box::new(Expr::Variable("x".into())),
        }),
        else_branch: Box::new(Expr::Number(0.0)),
    };

    assert_eq!(cond_expr.eval(&env).unwrap(), 9.0);  // x=3, 3*3=9
}

10.5 设计要点回顾

技术在本实战中的应用
enumExpr 递归定义 AST 节点;BinOp/UnOp/EvalError 表达有限选择
structBasicEnv 组织变量和函数存储
Box<T>Expr 递归引用必须用 Box(间接引用,固定大小)
traitEnvironment 解耦求值环境,可替换为其他实现
dyn Trait函数表存储 Box<dyn Fn>,支持异构函数
match穷举处理所有 Expr 变体和运算符
Result错误传播,? 运算符简化代码
impl ... for为 Expr 实现方法,为 BasicEnv 实现 Environment

✅ 为什么不用 trait object 表达 AST?:用 enum 表达 AST 是 Rust 惯用法——变体有限且已知,match 穷举保证安全,零运行时开销。如果需要开放扩展(第三方添加节点类型),才考虑 trait + Box<dyn>