第 1 章 结构体与方法
1.1 定义与实例化
结构体(struct)把相关的数据组合成一个自定义类型:
rust
struct User {
username: String, // 字段: 类型
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("xiaoming"),
active: true,
sign_in_count: 1, // 字段顺序无所谓
};
// 访问字段用点号
println!("用户名:{}", user1.username);
// 整个实例可变,才能改字段(Rust 不支持只改部分字段的可变性)
let mut user2 = User {
username: String::from("xiaohong"),
email: String::from("hong@example.com"),
active: false,
sign_in_count: 0,
};
user2.email = String::from("new@example.com");
}
1.2 三个便捷语法
字段初始化简写
rust
fn build_user(email: String, username: String) -> User {
User {
email, // 等价于 email: email
username, // 变量名与字段名相同时可简写
active: true,
sign_in_count: 1,
}
}
结构体更新语法
rust
let user3 = User {
email: String::from("another@example.com"),
..user1 // 其余字段从 user1 搬过来
};
// ⚠️ 注意:String 字段是移动,user1 中没显式列出的 String 字段会被移走
// 本例中 username、active、sign_in_count 来自 user1
// username 是 String → 移动后 user1.username 不能再用了
元组结构体与单元结构体
rust
struct Color(i32, i32, i32); // 元组结构体:有名字但没有字段名
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
// black 和 origin 是不同类型,不能混用------这是它存在的意义
struct AlwaysEqual; // 单元结构体:没有任何字段
let _subject = AlwaysEqual; // 常用于实现 trait 的标记类型
1.3 一个演进示例:计算长方形面积
体会结构体如何让代码语义更清晰。
版本 1:散装的变量
rust
fn area(width: u32, height: u32) -> u32 {
width * height
}
// 问题:width 和 height 明明是一体的,却毫无关联
版本 2:元组
rust
fn area(dimensions: (u32, u32)) -> u32 {
dimensions.0 * dimensions.1 // 问题:.0 和 .1 是什么?读不懂
}
版本 3:结构体 ✅
rust
struct Rectangle {
width: u32,
height: u32,
}
fn area(rect: &Rectangle) -> u32 { // 借用,不夺取所有权
rect.width * rect.height
}
fn main() {
let rect = Rectangle { width: 30, height: 50 };
println!("面积 = {}", area(&rect)); // 1500
}
打印结构体:derive(Debug)
rust
#[derive(Debug)] // 让编译器自动实现 Debug
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect = Rectangle { width: 30, height: 50 };
println!("{:?}", rect); // Rectangle { width: 30, height: 50 }
println!("{:#?}", rect); // 美化版,每个字段一行
dbg!(&rect); // 调试宏:连文件名行号一起打印到 stderr
}
1.4 方法(method)
方法定义在 impl(implementation)块里,第一个参数是 self:
rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// &self 是 self: &Self 的简写------借用实例
fn area(&self) -> u32 {
self.width * self.height
}
// 可以改自己:&mut self
fn double(&mut self) {
self.width *= 2;
self.height *= 2;
}
// 可以吃掉自己:self(转移所有权,调用后实例失效)
fn destroy(self) {
println!("{:?} 被销毁", self);
}
// 判断能否容纳另一个长方形
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let mut rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
println!("面积 = {}", rect1.area()); // 1500,方法调用语法
println!("能容纳吗?{}", rect1.can_hold(&rect2)); // true
rect1.double();
println!("翻倍后面积 = {}", rect1.area()); // 6000
}

self 的三种形态:
&self:只读借用(最常用)&mut self:可变借用(要修改字段)self:拿走所有权(用于"转换/销毁"场景)这依然遵守所有权规则------方法签名就把"这个方法对实例做什么"表达得清清楚楚。
1.5 关联函数(类似其他语言的"静态方法")
没有 self 参数的 impl 函数叫关联函数,用 :: 调用:
rust
impl Rectangle {
// 构造正方形------一种常见的"构造函数"惯例
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
fn main() {
let sq = Rectangle::square(20); // 用 :: 而不是 .
println!("正方形面积 = {}", sq.area()); // 400
}
String::from、String::new 都是关联函数。一个结构体可以有多个 impl 块。
小结
struct组合相关数据;实例整体可变或整体不可变- 便捷语法:字段简写、
..base更新语法、元组结构体 #[derive(Debug)]+{:?}/dbg!打印结构体impl块里定义方法(&self/&mut self/self)和关联函数(Type::func())- 结构体存引用需要生命周期,暂用拥有所有权的类型
第 2 章 枚举与模式匹配
2.1 枚举:一个值是几种可能之一
rust
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
// 枚举可以做函数参数类型
fn route(ip_kind: IpAddrKind) { }
2.2 枚举变体可以携带数据(强大之处)
rust
enum IpAddr {
V4(u8, u8, u8, u8), // 每个变体能带不同类型、不同数量的数据!
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
一个更复杂的例子------消息枚举(替代多个结构体):
rust
enum Message {
Quit, // 无数据
Move { x: i32, y: i32 }, // 像结构体一样的命名字段
Write(String), // 带一个 String
ChangeColor(i32, i32, i32), // 带三个 i32
}
// 枚举也能有方法!
impl Message {
fn call(&self) {
println!("消息被处理了");
}
}
fn main() {
let m = Message::Write(String::from("hello"));
m.call();
}
2.3 Option:Rust 没有 null
很多语言的 null 被称为"十亿美元的错误"。Rust 干脆没有 null,用标准库枚举 Option<T> 表达"可能有值、可能没有":
rust
// 标准库里的定义(不用自己写):
// enum Option<T> {
// Some(T),
// None,
// }
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None; // None 需要标注类型
关键点 :Option<i32> 和 i32 是不同类型,不能直接运算------编译器强迫你先处理"可能没有值"的情况:
rust
let x: i32 = 5;
let y: Option<i32> = Some(5);
// let sum = x + y; // ❌ 编译错误!
let sum = x + y.unwrap_or(0); // ✅ 有值用值,没值用 0
常用方法:
rust
let v = Some(42);
v.unwrap_or(0); // 有值取值,无值用默认值 → 42
v.is_some(); // true
v.is_none(); // false
v.map(|n| n * 2); // 有值则变换 → Some(84)
// v.unwrap(); // 有值取值,None 时 panic!(谨慎使用)
2.4 match:模式匹配
match 像一个超级 switch,但强大得多:
rust
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("幸运币!");
1 // 分支可以是代码块,最后表达式是返回值
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
绑定值的模式
rust
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState), // Quarter 携带州名
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Quarter(state) => { // state 绑定了枚举里的数据
println!("来自 {:?} 的 25 美分!", state);
25
}
_ => 1, // 简化示例
}
}
match 必须穷尽所有可能
rust
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1), // i 绑定 Some 内部的值
}
}
// 如果漏掉 None 分支 → ❌ 编译错误:non-exhaustive patterns
编译器替你检查没有遗漏任何情况------重构时加一个枚举变体,所有没处理新情况的 match 都会报错,这是大型项目的安全保障。
_ 通配符
rust
let dice = 9;
match dice {
3 => println!("三"),
7 => println!("七"),
_ => println!("其他"), // 匹配其余所有值
}
// 也可以匹配具体值范围、多值
match dice {
1 | 2 => println!("小"), // | 表示"或"
3..=4 => println!("中"), // 范围
_ => println!("大"),
}
2.5 if let:只关心一种情况的简写
rust
let some_value = Some(3u8);
// match 写法(啰嗦)
match some_value {
Some(3) => println!("是三!"),
_ => (),
}
// if let 写法(清爽)
if let Some(3) = some_value {
println!("是三!");
}
// 还能带 else
if let Some(n) = some_value {
println!("有值:{}", n);
} else {
println!("没有值");
}
if let本质上就是只处理一个分支的match的语法糖。会失去穷尽性检查,取舍由你。
2.6 综合示例:交通灯状态机
rust
enum TrafficLight {
Red,
Yellow,
Green,
}
impl TrafficLight {
fn duration(&self) -> u32 { // 各灯持续秒数
match self {
TrafficLight::Red => 60,
TrafficLight::Yellow => 5,
TrafficLight::Green => 45,
}
}
fn next(&self) -> TrafficLight { // 下一个状态
match self {
TrafficLight::Red => TrafficLight::Green,
TrafficLight::Yellow => TrafficLight::Red,
TrafficLight::Green => TrafficLight::Yellow,
}
}
fn can_pass(&self) -> bool {
match self {
TrafficLight::Green => true,
_ => false,
}
}
}
fn main() {
let mut light = TrafficLight::Red;
for _ in 0..6 {
let name = match light {
TrafficLight::Red => "红",
TrafficLight::Yellow => "黄",
TrafficLight::Green => "绿",
};
println!("{}灯,持续 {} 秒,{}通行",
name,
light.duration(),
if light.can_pass() { "可以" } else { "禁止" });
light = light.next();
}
}
输出:

小结
enum表示"几种可能之一",每个变体可携带不同类型数据Option<T>(Some/None)取代 null,编译器强制处理"无值"情况match模式匹配,必须穷尽 ,可绑定值、用_通配、|多值、范围if let/if let ... else处理单一模式的简写