结构体组织数据,枚举表达选择,Trait 定义行为——三者结合,就是 Rust 类型系统的表达力核心。掌握它们,你才能写出编译器帮你检查一切的代码。
结构体是 Rust 中组织命名字段的主要方式。与 C 结构体类似但更严格——没有未初始化字段,没有继承,取而代之的是组合和 trait。
// 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 的"最小可见性"哲学。
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 就不再可用。
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)); // ✅
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> 是不同类型!
#[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)
}
}
Rust 没有 class,方法通过 impl 块绑定到类型上。数据和行为分开定义,但保持关联。
#[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 不可用 |
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 Counter 中 Self = Counter。写 Self 而非 Counter,方便后续重构。
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);
// 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) { /* ... */ }
}
Rust 的枚举远比 C/Java 强大——每个变体可以携带不同类型和数量的数据。枚举 + match 是 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 更简洁、更安全,编译器保证你处理了所有情况。
// 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
fn get(&self, key: &str) -> Option<&Value>
// 编译器强制处理 None
match map.get("foo") {
Some(v) => use(v),
None => handle_missing(),
}
// Java: NullPointerException 运行时爆炸
Value v = map.get("foo");
v.doSomething(); // 💥 if null
// Go: 忘记检查 error
v, _ := map.Get("foo")
v.DoSomething() // 💥 if nil
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);
}
match 只是最常见的场景。let 绑定、函数参数、for 循环都可以解构。
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),
}
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);
}
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"),
}
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 条件是否覆盖所有情况,所以使用守卫时通常需要 _ 兜底。
泛型让你写一次代码适用于多种类型。Rust 的泛型通过单态化(monomorphization)在编译时展开,运行时零成本。
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');
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>> },
}
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> 是完全不同的类型,编译器为每种使用到的类型参数生成一份专门代码。没有虚函数表,没有运行时查找。
Trait 定义类型能做什么,impl ... for ... 提供具体实现。Trait 是 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)
}
}
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() 使用默认实现
}
// 方式 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 { /* ... */ }
// 返回 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() })
}
}
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())
}
}
| 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 |
#[derive(Debug)]
struct User { name: String, age: u32 }
println!("{:?}", user);
// User { name: "Alice", age: 30 }
println!("{:#?}", user);
// 美化多行输出
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)
let s1 = String::from("hello");
let s2 = s1.clone(); // 显式调用
// s1 仍然可用
let x = 42;
let y = x; // i32: Copy,隐式复制
let s1 = String::from("hello");
let s2 = s1; // String: 移动!
// s1 不可用了
⚠️ Copy 的前提:所有字段都必须是 Copy 的。String 不是 Copy(持有堆内存),所以含 String 字段的结构体也不能 Copy。
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 进行转换
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
impl Trait 是静态分发(编译时确定类型),dyn Trait 是动态分发(运行时查虚函数表)。两者互补,各有所长。
fn process(v: &impl Summary) {
v.summarize();
}
// 编译器为每种类型生成一份代码
// 零运行时开销
// 二进制可能更大(单态化)
fn process(v: &dyn Summary) {
v.summarize();
}
// 通过 vtable 运行时间接调用
// 微小运行时开销
// 二进制更小
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();
// ❌ 不对象安全:返回 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 时提前检查。
// 默认泛型参数隐式有 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
// 为现有类型创建新的独立类型
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(", "))
}
}
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>;
struct Meters(u32); // Meters ≠ u32(类型安全) // 需要手动实现所有 trait // 可以添加额外方法
type Meters = u32; // Meters = u32(只是别名) // 继承原类型所有实现 // 零开销,但无类型安全
// ! 类型:永远不返回的函数的返回类型
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, // ! → 任何类型
};
}
// 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)。
这个实战综合运用了 struct、enum、trait、泛型、模式匹配——构建一个支持变量和函数的 AST 求值器。
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 }
/// 环境接口 — 解耦变量/函数存储
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)
}
}
#[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)
}
}
}
}
}
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
}
| 技术 | 在本实战中的应用 |
|---|---|
enum | Expr 递归定义 AST 节点;BinOp/UnOp/EvalError 表达有限选择 |
struct | BasicEnv 组织变量和函数存储 |
Box<T> | Expr 递归引用必须用 Box(间接引用,固定大小) |
trait | Environment 解耦求值环境,可替换为其他实现 |
dyn Trait | 函数表存储 Box<dyn Fn>,支持异构函数 |
match | 穷举处理所有 Expr 变体和运算符 |
Result | 错误传播,? 运算符简化代码 |
impl ... for | 为 Expr 实现方法,为 BasicEnv 实现 Environment |
✅ 为什么不用 trait object 表达 AST?:用 enum 表达 AST 是 Rust 惯用法——变体有限且已知,match 穷举保证安全,零运行时开销。如果需要开放扩展(第三方添加节点类型),才考虑 trait + Box<dyn>。