基础语法:变量、类型与模式匹配
上一篇我们聊了为什么 Rust 值得学,以及怎么跑通你的第一个 Hello World。这一篇直接上手语法——变量、类型、控制流、函数、模式匹配。核心目标只有一个:让你能读懂和写出 Rust 代码。
如果你有 Go 的基础,这里的每个概念对你来说都不会陌生。我会在关键节点做对照,帮你把已知的知识映射过来。
变量声明
Rust 的变量声明非常简洁,核心原则是默认不可变,需要修改时加 mut:
| |
Go/Rust 对照:
| Go | Rust | |
|---|---|---|
| 不可变绑定 | const x = 5 (包级) 或 x := 5 (函数内) | let x = 5 |
| 可变绑定 | x := 5 | let mut x = 5 |
| 变量遮蔽 | x := 5; x := 10 (语法错误) | let x = 5; let x = 10 (OK) |
| 编译期常量 | const PI = 3.14 | const PI: f64 = 3.14 |
Go 没有真正意义上的不可变绑定——const 只能用于包级常量,函数内的 := 声明的变量默认可变。Rust 则是默认不可变,需要显式标注 mut 才能修改。
变量遮蔽是 Rust 的一个独特特性。Go 不允许同名变量遮蔽,但 Rust 允许通过新的 let 创建同名绑定,甚至可以改变类型。这在数据转换场景下很实用,比如先把字符串解析成数字,再进行计算。
const 在 Go 和 Rust 中都是编译期常量,但 Rust 的 const 必须显式标注类型。
基本数据类型
Rust 的类型系统在保留传统分类的同时,引入了更严格的类型约束:
| 类型类别 | Rust 类型 | 说明 |
|---|---|---|
| 有符号整数 | i8, i16, i32, i64, i128, isize | isize = 平台相关有符号整数 |
| 无符号整数 | u8, u16, u32, u64, u128, usize | usize = 平台相关无符号整数 |
| 浮点数 | f32, f64 | IEEE 754 标准 |
| 布尔值 | bool | true / false |
| 字符 | char | Unicode 标量值(4 字节) |
| 字符串 | &str, String | 字符串切片 / 堆分配字符串 |
| 元组 | (T1, T2, T3) | 固定长度异构集合 |
| 数组 | [T; N] | 固定长度同构集合 |
Go 开发者最该注意的差异是字符类型。Rust 的 char 是 Unicode 标量值(4 字节),可以表示任何 Unicode 字符,而 Go 的 rune 也是 4 字节的 Unicode 码点。但 Go 的 byte 是 uint8 的别名(1 字节),Rust 没有单独的 byte 类型,直接用 u8。
isize 和 usize 是平台相关的整数类型,对应指针大小——32 位系统上是 32 位,64 位系统上是 64 位。这和 Go 的 int / uint 类似。
字符串类型有两个:&str 是字符串切片(不可变, borrowed),String 是堆分配的字符串(可变,owned)。这和 Go 的 string(不可变)+ []byte(可变)的设计不同,Rust 通过借用规则来保证内存安全,不区分 UTF-8 字节序列和字符串类型。
元组类型用圆括号表示,比如 (i32, f64, bool)。Go 没有内置的元组类型,但可以通过结构体或多个返回值模拟。
| |
控制流
if 表达式
Rust 的 if 是表达式——可以返回值:
| |
Go 的 if 是语句,不能返回值;Rust 的 if 是表达式。注意 if 表达式必须返回相同类型的值。
| |
Rust 的条件必须是 bool 类型,不会像 C/Go 那样自动把非零值当作 true。
match 表达式
match 是 Rust 的杀手级特性,比 Go 的 switch 强大得多:
| |
Go/Rust 对照:
| Go | Rust | |
|---|---|---|
| 分支穿透 | 默认穿透,需 break 阻止 | 默认不穿透,每个分支独立 |
| 必须穷尽 | 不必,有 default | 必须,编译器检查 |
| 模式匹配 | 有限(type switch) | 强大(结构、范围、Option 等) |
| 表达式 | 不是表达式 | 是表达式,可返回值 |
Rust 的 match 必须穷尽所有可能,否则编译失败。这保证了不会遗漏任何情况,避免了 Go 中常见的"忘记 default"的 bug。
match 的每个分支用 => 连接模式和表达式。_ 是通配符,匹配所有未列出的情况。范围匹配用 ..= 表示闭区间,比 Go 的 case 更直观。
Option<T> 是 Rust 的标准库类型,表示"有值或无值",比 Go 的 (T, error) 更轻量。Some(T) 表示有值,None 表示无值。通过 match 可以优雅地处理这两种情况。
循环
Rust 有三种循环:loop、while、for:
| |
Go/Rust 对照:
| 场景 | Go | Rust |
|---|---|---|
| 无限循环 | for { ... } | loop { ... } |
| 条件循环 | for condition { ... } | while condition { ... } |
| 遍历集合 | for i, v := range items | for (i, v) in items.iter().enumerate() |
| 索引循环 | for i := 0; i < n; i++ | for i in 0..n |
Rust 的 for 循环基于迭代器,比 Go 的 range 更通用。通过迭代器模式,可以遍历任何实现了 IntoIterator trait 的类型。
loop 是 Rust 的特色——无限循环必须有显式的 break,这避免了 Go 中常见的"忘记 break"的死循环 bug。break 甚至可以返回值,这在某些场景下很实用。
函数
Rust 用 fn 关键字定义函数,参数必须有类型标注,返回值类型在 -> 后:
| |
Go/Rust 对照:
| 特性 | Go | Rust |
|---|---|---|
| 函数签名 | func add(a, b int) int | fn add(a: i32, b: i32) -> i32 |
| 多返回值 | 原生支持 | 通过元组 |
| 命名返回值 | 支持 | 不支持 |
| 返回值省略 | 可省略(有返回值类型) | 最后一个表达式自动返回 |
Rust 没有命名返回值,但通过表达式返回值更简洁。Go 的命名返回值可以在函数体中直接赋值,但 Rust 需要显式返回。
语句 vs 表达式
这是 Rust 和 Go 最大的语法差异之一。Rust 区分语句(statement)和表达式(expression):
- 语句:执行某些操作但不返回值,以分号
;结尾 - 表达式:计算并返回值,不以分号结尾
| |
Go 中几乎所有东西都是语句,只有少数例外(如 func literal)。Rust 中 if、match、loop、{} 块都可以是表达式,这让代码更简洁。
关键规则:给变量赋值的表达式不能以分号结尾,否则就变成了语句,返回 ()(unit type)。
| |
引用与切片基础
Rust 的引用系统是其所有权模型的基础。引用允许你在不获取所有权的情况下访问值:
| |
&s1 创建对 s1 的不可变引用,s 的类型是 &String。函数体结束,引用失效,但 s1 的所有权不变。
切片语法 [start..end] 创建对集合的切片视图。[0..5] 表示从索引 0 开始(包含)到索引 5 结束(不包含)。.. 可以省略开始或结束:..5 表示从 0 到 5,6.. 表示从 6 到末尾。
Go/Rust 对照:
| Go | Rust | |
|---|---|---|
| 不可变引用 | &T 或指针 *T | &T |
| 可变引用 | *T | &mut T |
| 字符串切片 | s[start:end] | &s[start..end] |
| 借用规则 | 无约束 | 同一作用域内,多个不可变引用 或 一个可变引用 |
Go 没有借用规则,可以同时有多个可变引用。Rust 的借用规则保证了编译时内存安全,这是 Rust 最核心的设计之一。
所有权入门预告
Rust 的所有权(ownership)是其最独特的特性,确保内存安全而无需垃圾回收。基本规则:
- Rust 中的每个值都有一个所有者(owner)
- 值在任一时刻有且只有一个所有者
- 当所有者离开作用域,值被丢弃
| |
字符串类型 String 是堆分配的,赋值时默认是移动(move)语义,不是浅拷贝。Go 的字符串赋值是浅拷贝(共享底层数组),Rust 需要显式调用 .clone() 才能深拷贝。
所有权的完整内容我们留到下一篇详细讲,包括借用(borrowing)、生命周期(lifetime)等核心概念。这里先有个印象即可——Rust 通过所有权在编译时保证内存安全,不需要垃圾回收。
本篇小结
到此我们已经覆盖了 Rust 基础语法的核心——变量声明、基本数据类型、控制流、函数、语句 vs 表达式、引用与切片、所有权入门。你现在已经能读懂大部分 Rust 代码了。
如果记住一句话就够了:Rust 的语法兼顾了表达力和安全性——变量默认不可变,match 强大且必须穷尽,if/match/loop 都是表达式,借用规则在编译时保证内存安全。
下一篇我们深入 Rust 最独特的特性:所有权与借用——所有权规则、引用、生命周期、Copy trait。这是 Rust 安全编程的基石,也是 Go 开发者最需要适应的地方。