结构体(Struct)
定义与实例化
结构体是 Rust 中组织相关数据的基本方式,类似于其他语言中的类(Class),但 Rust 将数据定义(struct)和行为定义(impl)明确分离:
// 定义结构体
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// 创建结构体实例(所有字段必须初始化)
let mut user1 = User {
email: String::from("user@example.com"),
username: String::from("rustacean"),
active: true,
sign_in_count: 1,
};
// 访问和修改字段(必须整个实例是 mut)
user1.email = String::from("new@example.com");
// 结构体更新语法:用已有实例创建新实例
let user2 = User {
email: String::from("another@example.com"),
..user1 // 其余字段来自 user1(注意:会 move user1 中的 String 字段)
};
println!("{}", user2.username);
}
元组结构体与单元结构体
// 元组结构体:字段没有名字,通过索引访问
struct Color(u8, u8, u8); // RGB
struct Point(f64, f64, f64); // 3D 坐标
// 单元结构体:没有任何字段,用于实现 Trait 时
struct AlwaysEqual;
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0, 0.0);
println!("Red: {}, Green: {}, Blue: {}", black.0, black.1, black.2);
}
方法(impl 块)
#[derive(Debug)]
struct Rectangle {
width: f64,
height: f64,
}
// impl 块:为结构体定义方法和关联函数
impl Rectangle {
// 关联函数(类似 static 方法):没有 &self 参数
pub fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
// 方法:第一个参数是 &self(不可变借用)
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
// 可变方法:第一个参数是 &mut self
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
// 方法可以接受其他实例的引用
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let mut rect = Rectangle::new(10.0, 5.0);
println!("面积: {:.1}", rect.area()); // 50.0
println!("周长: {:.1}", rect.perimeter()); // 30.0
rect.scale(2.0);
println!("{:?}", rect); // Rectangle { width: 20.0, height: 10.0 }
}
枚举(Enum)
枚举:比其他语言强大得多
Rust 的枚举远比 C/Java 中的枚举强大——每个枚举变体可以携带不同类型和数量的数据,这使得 Rust 枚举成为一种代数数据类型(Algebraic Data Type, ADT),能精确表达业务逻辑中所有可能的状态。
// 简单枚举
enum Direction { North, South, East, West }
// 携带数据的枚举——每个变体可以有不同的数据类型
#[derive(Debug)]
enum Message {
Quit, // 无数据
Move { x: i32, y: i32 }, // 匿名结构体
Write(String), // 单个字符串
ChangeColor(u8, u8, u8), // 三个 u8
}
impl Message {
fn call(&self) {
// 稍后用 match 处理每种情况
println!("{:?}", self);
}
}
fn main() {
let msgs = vec![
Message::Quit,
Message::Move { x: 10, y: 20 },
Message::Write(String::from("hello")),
Message::ChangeColor(255, 128, 0),
];
for msg in &msgs { msg.call(); }
}
Option<T>:用枚举消灭空指针
Rust 没有 null。这是有意为之的设计决策——null 引用的发明者 Tony Hoare 自称这是"billion-dollar mistake"(价值十亿美元的错误),因为它在全球软件系统中造成了无数崩溃和漏洞。Rust 用标准库中的 Option<T> 枚举来表示"可能不存在的值":
// 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() {
let result = divide(10.0, 2.0);
// 处理 Option 值的几种方式:
// 1. match(最完整)
match result {
Some(v) => println!("结果: {}", v),
None => println!("除以零"),
}
// 2. if let(只关心一种情况时)
if let Some(v) = divide(10.0, 3.0) {
println!("得到: {:.4}", v);
}
// 3. unwrap_or:提供默认值(发生 None 时)
let safe = divide(1.0, 0.0).unwrap_or(0.0);
// 4. ?运算符(在函数中传播)—— 见第6章
// 5. map:变换 Some 中的值
let doubled = divide(10.0, 2.0).map(|v| v * 2.0);
println!("doubled: {:?}", doubled); // Some(10.0)
}
模式匹配(Pattern Matching)
match:穷举的力量
Rust 的 match 表达式要求穷举所有可能的情况——编译器会检查你是否覆盖了枚举的所有变体。这个"穷举性检查"是 Rust 安全性的重要组成部分:当你添加新的枚举变体时,编译器会立即提醒你更新所有 match 语句。
#[derive(Debug)]
enum Coin { Penny, Nickel, Dime, Quarter }
fn value_in_cents(coin: Coin) -> u32 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
// 如果漏掉一个,编译器报错
}
}
fn describe_number(n: i32) -> &'static str {
match n {
1 => "一",
2 | 3 => "二或三", // | 匹配多个值
4..=9 => "四到九", // 范围匹配
n if n < 0 => "负数", // 匹配守卫(guard)
_ => "其他", // _ 是通配符
}
}
解构匹配
fn process_message(msg: Message) {
match msg {
Message::Quit => {
println!("退出");
}
Message::Move { x, y } => { // 解构结构体变体
println!("移动到 ({}, {})", x, y);
}
Message::Write(text) => { // 绑定内部值
println!("写入: {}", text);
}
Message::ChangeColor(r, g, b) => {
println!("颜色: #{:02X}{:02X}{:02X}", r, g, b);
}
}
}
// 解构结构体
let point = Point { x: 5, y: 10 };
let Point { x, y } = point;
println!("x={}, y={}", x, y);
// 解构元组
let (a, b, c) = (1, 2, 3);
// 忽略部分字段
let Point { x, .. } = point; // 只绑定 x,忽略其他字段
if let 与 while let
fn main() {
let config_max = Some(3u8);
// if let:只关心一种 pattern 时,比 match 简洁
if let Some(max) = config_max {
println!("最大值: {}", max);
}
// if let 可以带 else
if let Some(max) = config_max {
println!("有值: {}", max);
} else {
println!("没有值");
}
// while let:条件为 pattern 匹配时持续循环
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
print!("{} ", top); // 3 2 1
}
}
让非法状态不可表示
Rust 枚举的强大之处在于可以利用类型系统"让非法状态不可表示"(Making Illegal States Unrepresentable)。例如,用 Option<Email> 而不是 String 来表示可选的邮箱地址,这样类型本身就告诉了你这个字段可能不存在,而编译器强制你处理这种情况。这是领域驱动设计(DDD)思想在类型系统中的体现。