🦀 Rust学习教程2:基础语法
面向 C++ 工程师的 Rust 基础语法精讲:变量与类型、函数、控制流,辅以与 C++ 的对比、FAQ、练习与总结。读完即可动手写简单程序。

📑 目录
- [零、🚀 从 Hello World 开始](#零、🚀 从 Hello World 开始)
- [一、📦 变量与类型](#一、📦 变量与类型)
- [二、⚙️ 函数](#二、⚙️ 函数)
- [三、🔄 控制流](#三、🔄 控制流)
- [四、💬 表达式与语句](#四、💬 表达式与语句)
- [五、🆚 与 C++ 的区别](#五、🆚 与 C++ 的区别)
- [六、❓ FAQ 常见问题](#六、❓ FAQ 常见问题)
- [七、✏️ 练习](#七、✏️ 练习)
- [八、✅ 总结](#八、✅ 总结)
零、🚀 从 Hello World 开始
确保已安装 rustup 和 Cargo,在终端执行:
bash
cargo new rust_basics
cd rust_basics
在 src/main.rs 中已有默认入口。本教程涉及的语法都可在该项目的 main.rs 或新文件中练习。
rust
fn main() {
println!("Hello, world!");
}
运行:cargo run。下面所有示例均可放在 main 中或单独函数里调用。
本教程知识结构概览:
🦀 Rust 基础语法
变量与类型
let / let mut
标量 整型 浮点 bool char
元组与数组
常量与遮蔽
函数
参数与返回类型
表达式返回值
发散函数
控制流
if / else 表达式
loop while for
表达式与语句
块与分号
注释与打印
📁 cargo new
✏️ 写代码
▶️ cargo run
一、📦 变量与类型
Rust 是静态类型 语言,类型在编译期确定;同时支持类型推断 ,多数情况下不必手写类型。变量默认不可变 ,需要可变时显式写 mut。
1.1 🔒 变量声明:let 与 let mut
rust
fn main() {
let x = 42; // 不可变,类型推断为 i32
let mut y = 10; // 可变
y += 1; // OK
// x += 1; // 编译错误:cannot assign twice to immutable variable
}
记忆要点 :默认不可变,要改就加 mut。
变量
let x = 42
🔒 不可变
let mut y = 10
🔓 可变
可变何时用
需要原地修改时
let mut 显式声明
不可变的好处
编译期保证不改
便于推理与并发
1.2 📊 标量类型(Scalar)
| 类型 | 说明 | 示例 | 与 C++ 对照 |
|---|---|---|---|
| 整型 | 有符号 / 无符号,固定位宽 | i32, u64, i8 |
int32_t, uint64_t |
| 浮点 | f32, f64 |
3.14_f32, 2.0 |
float, double |
| 布尔 | bool |
true, false |
bool |
| 字符 | char,Unicode 标量 |
'中', 'a' |
char(Rust 为 4 字节) |
整型字面量:可加类型后缀与分隔符:
rust
let a = 42_i32; // 有符号 32 位
let b = 0xff_u8; // 十六进制,无符号 8 位
let c = 1_000_000; // 可读性分隔
默认推断 :未指定时,整数推断为 i32,浮点数推断为 f64。
其他标量
f32 f64
bool
char
整型
i8 i16 i32 i64 i128
u8 u16 u32 u64 u128
1.3 🧩 复合类型:元组与数组
元组(Tuple):固定长度、可异构。
rust
let t: (i32, f64, bool) = (42, 3.14, true);
let first = t.0; // 42,通过 .0 .1 .2 访问
let (x, y, z) = t; // 解构
数组(Array):固定长度、同类型,长度是类型的一部分。
rust
let arr: [i32; 5] = [1, 2, 3, 4, 5]; // 类型为 [i32; 5]
let zeros = [0; 10]; // [0, 0, 0, ...],10 个 0
let first = arr[0]; // 下标访问,越界会 panic
与 C++ 对照:Rust 的数组更像 std::array<T, N>,长度固定;动态数组用 Vec<T>(后续阶段会学)。
复合
(T, U, ...) 元组
T; N 数组
标量
i8..i128, u8..u128
f32, f64
bool, char
类型
1.4 🌑 常量与变量遮蔽(含作用域)
常量(const):编译期确定,必须带类型,命名习惯全大写+下划线。
rust
const MAX_POINTS: u32 = 100_000;
变量遮蔽(shadowing) :用 let 再次声明同名变量,新变量可换类型、可覆盖旧值,与 mut 不同(mut 是原地改,不换类型)。
官方书中的要点:第二个变量会「遮蔽」第一个,直到它自己再被遮蔽或所在作用域结束;离开内层作用域后,外层绑定重新可见。
下面用 println! 演示:同一作用域内遮蔽、以及内层作用域遮蔽后离开作用域的情况。
rust
fn main() {
let x = 5;
println!("1. 外层 x = {}", x); // 1. 外层 x = 5
let x = x + 1; // 同一作用域内遮蔽,x 变为 6
println!("2. 遮蔽后 x = {}", x); // 2. 遮蔽后 x = 6
{
let x = x * 2; // 内层作用域遮蔽:新 x = 12
println!("3. 内层作用域 x = {}", x); // 3. 内层作用域 x = 12
} // 内层 x 离开作用域,被丢弃
println!("4. 离开内层后 x = {}", x); // 4. 离开内层后 x = 6
}
运行输出:
text
1. 外层 x = 5
2. 遮蔽后 x = 6
3. 内层作用域 x = 12
4. 离开内层后 x = 6
作用域与遮蔽关系(谁可见、何时恢复):
内层作用域 x 外层作用域 x 内层作用域 x 外层作用域 x let x = 5 let x = x + 1 let x = x * 2 (遮蔽) 块结束,内层 x 丢弃 println → 5 println → 6 println → 12 println → 6 (外层 x 重新可见)
内层块 {}
外层
x = 5
let x = x+1 → x = 6
离开内层后 x 仍是 6
let x = x*2 → x = 12
与 C++ 对比:C++ 没有「同一作用域内同名遮蔽」的惯用写法;Rust 的 shadowing 常用于转换类型或中间变量,且内层遮蔽不会改变外层的绑定,离开内层后外层变量恢复可见。
换类型的典型用法(仍用官方书例子):
rust
fn main() {
let spaces = " ";
let spaces = spaces.len(); // 遮蔽并换类型:&str → usize
println!("spaces 数量: {}", spaces); // 3
}
1.5 ✍️ 类型注解何时需要写?
编译器能推断时可以不写;以下情况建议或必须写:
- 函数参数与返回值(见下一节)
- 无法从上下文推断时(如
let v: Vec<i32> = vec![];) - 提高可读性时
rust
let x = 42; // 推断为 i32
let f = 3.14; // 推断为 f64
let explicit: u64 = 100; // 显式指定 u64
是
否
有足够上下文?
可省略类型
必须写类型
函数参数/返回值/空容器等
二、⚙️ 函数
Rust 用 fn 定义函数,参数和返回值类型必须写;最后一行的表达式 若无分号则作为返回值(无需写 return)。
2.1 📌 基本形态
rust
fn add(a: i32, b: i32) -> i32 {
a + b // 表达式,无分号,即返回值
}
fn with_return(x: i32) -> i32 {
if x > 0 {
return x * 2; // 提前返回时用 return
}
0
}
要点:
- 参数:
名字: 类型,多个用逗号分隔。 - 返回值:
-> 类型;无返回值可省略或写-> ()。 - 最后一行为表达式(无分号)时,该表达式的值即为函数返回值。
fn name
参数: 类型
-> 返回类型
函数体
最后表达式 = 返回值
无分号 表达式
有 return
分号/语句
函数调用
求值参数
执行函数体
最后一行
该值即返回值
返回
2.2 💡 表达式与语句(为何无分号就能返回?)
Rust 中:
- 语句:执行操作,不产生值,以分号结尾。
- 表达式 :产生值;若在函数体末尾且没有分号,该值就是函数返回值。
rust
fn demo() -> i32 {
let x = 1; // 语句
let y = { // 块也是表达式
let z = 2;
z + 1 // 块的值是 3,无分号
};
y + x // 函数返回值 4,无分号
}
所以「无 return 也能返回」是因为最后一行是表达式。
2.3 ♾️ 发散函数(永不返回)
若函数永不返回(如无限循环或 panic!),返回类型写 !(never type):
rust
fn forever() -> ! {
loop {}
}
2.4 📋 小结速查
| 写法 | 含义 |
|---|---|
fn f(x: i32) -> i32 { x } |
单参数,返回最后一行的值 |
fn f() -> () { } 或 fn f() { } |
无返回值 |
return x; |
提前返回,可写可不写(末尾表达式更常见) |
三、🔄 控制流
Rust 提供 if/else、loop、while、for,且 if 和块都是表达式,可赋值、可返回。
3.1 🔀 if / else
条件必须是 bool,不能像 C++ 那样用整数或指针。
rust
let n = 5;
if n < 0 {
println!("negative");
} else if n == 0 {
println!("zero");
} else {
println!("positive");
}
if 表达式:分支可产生值,类型须一致。
rust
let result = if n > 0 { "positive" } else { "non-positive" };
// result 为 &str
true
false
有
无
条件 bool
分支 1
下一条件?
else if
else
结果
3.2 🔁 loop:无限循环
loop 相当于 while true,必须用 break 或 return 退出;break 可带返回值(常用于重试直到成功)。
rust
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // 跳出并带出值 20
}
};
是
否
进入 loop
执行循环体
break?
break 可带值
loop 表达式得到该值
3.3 ⏳ while
与 C++ 类似,条件为 bool。
rust
let mut n = 3;
while n > 0 {
println!("{}", n);
n -= 1;
}
3.4 🏃 for:迭代器驱动
Rust 的 for 用于遍历迭代器,最常见的是范围与集合:
rust
// 范围:1..5 表示 1,2,3,4(不包含 5)
for i in 1..5 {
println!("{}", i);
}
// 包含上界:1..=5 表示 1 到 5
for i in 1..=5 {
println!("{}", i);
}
// 遍历数组
let arr = [10, 20, 30];
for x in arr {
println!("{}", x);
}
// 需要下标时
for (i, x) in arr.iter().enumerate() {
println!("index {}: {}", i, x);
}
与 C++ 对比:更像「范围 for」for (auto x : range),而不是 C 风格 for (int i=0; i<n; i++)。
循环
loop
break/return
while 条件
循环体
for x in 迭代器
循环体
1..5 或 1..=5
产生迭代项
for 绑定变量
执行循环体
3.5 📊 控制流小结
| 构造 | 用途 |
|---|---|
if / else if / else |
条件分支,可作表达式 |
loop |
无限循环,break 可带值 |
while 条件 |
条件循环 |
for x in 迭代器 |
遍历,推荐优先使用 |
四、💬 表达式与语句
理解「表达式 vs 语句」能减少「为什么这里不能加分号」的困惑。
语句
不产生值
let x=1;, 带分号的表达式
表达式
产生值
1+2, if/else 块, 块最后一行无分号
代码
| 概念 | 是否产生值 | 典型例子 |
|---|---|---|
| 语句 | 否 | let x = 1;、println!(...);、; 结尾的表达式 |
| 表达式 | 是 | 1 + 2、if x { 1 } else { 0 }、块的最后一行(无分号) |
块(block) :{ ... } 也是表达式,块的值 = 最后一条表达式的值(最后一行无分号)。
rust
let y = {
let x = 1;
x + 1 // 块的值是 2
};
无分号
有分号
块 { ... }
最后一行
块的值 = 该表达式
块的值 = ()
4.1 📝 注释与打印
注释 :与 C++ 类似,// 行注释,/* ... */ 块注释;文档注释用 ///(项上方)或 //!(模块/ crate 级别)。
rust
// 行注释
/* 块注释 */
/// 文档注释:说明下面函数的作用
fn f() {}
打印 :常用宏 println!、print!、eprintln!(标准错误);{} 为 Display,{:?} 为 Debug(调试输出),{:#?} 为带缩进的 Debug。
rust
let x = 42;
println!("x = {}", x);
println!("debug: {:?}", (1, true));
五、🆚 与 C++ 的区别
下表按「变量与类型、函数、控制流」做对照,便于从 C++ 迁移。
| 主题 | C++ | Rust |
|---|---|---|
| 变量默认 | 可改 | 默认不可变,需 let mut 才可改 |
| 类型 | 可隐式转换、可省略(auto) | 强类型,推断但少隐式转换 |
| 数组 | T[] / std::array<T,N> |
[T; N] 固定长度,越界 panic |
| 函数返回 | 必须 return 或构造返回值 |
最后表达式无分号即返回值 |
| 条件 | 可用整数、指针等 | 条件必须是 bool |
| 循环 | for(;;) / while / 范围 for |
loop / while / for x in 迭代器 |
| 无 return | 仅 void 函数或单表达式函数 |
任意函数都可用末尾表达式返回值 |
思维差异:
- C++:默认可变、多种所有权( raw 指针/引用/智能指针)。
- Rust:默认不可变、单一所有权,后续会学到借用与生命周期。

Rust 习惯
默认不可变
强类型 少隐式
表达式即返回
C++ 习惯
默认可变
隐式转换
return 显式
六、❓ FAQ 常见问题
Q1:为什么 let x = 1; 后面不能写 x = 2?
因为变量默认不可变。需要可改就写 let mut x = 1;。
Q2:函数最后一行加了分号,为什么报错「expected type, found ()」?
加分号后那一行变成语句,不产生值,相当于返回 ()。若函数声明返回 i32 等类型,类型不匹配就会报错。解决:去掉最后一行的分号,或显式 return 值;。
Q3:1..5 和 1..=5 有什么区别?
1..5 是左闭右开,即 1,2,3,4;1..=5 包含右端,即 1,2,3,4,5。
Q4:能否像 C++ 一样写 for (int i = 0; i < n; i++)?
可以但不推荐。需要索引时用 for i in 0..n { ... },或 iter().enumerate()。Rust 更推荐迭代器写法。
Q5:loop 和 while true 有区别吗?
语义上类似。loop 是专门语法,且 break 可带值(如 break 42),写法更简洁。
Q6:数组越界会怎样?
运行时 panic,程序终止。编译期仅在部分常量下标时能做检查。
Q7:如何指定整数类型避免推断成 i32?
字面量加后缀,如 0_u8、100_i64,或 let x: u64 = 100;。
Q8:变量遮蔽后,离开内层作用域会怎样?
内层遮蔽的变量随块结束被丢弃,外层的同名绑定重新可见(见 [1.4 变量遮蔽](#1.4 变量遮蔽) 的 println! 示例)。
七、✏️ 练习
建议在 Rust Playground 或本地 cargo new 项目中完成。
7.1 📦 变量与类型
- 声明不可变变量
pi: f64 = 3.14159,再声明可变变量count: i32,在循环中自增到 5 并打印。 - 定义一个长度为 4 的数组,用
for遍历并打印每个元素。 - 定义一个元组
(i32, bool, &str),分别用.0、.1、.2和解构两种方式取出值并打印。 - 遮蔽与作用域 :在外层定义
let x = 10,在内层块中let x = x * 2并打印,离开内层后再打印x,验证外层 x 仍为 10。
7.2 ⚙️ 函数
- 写函数
fn area_rect(w: f64, h: f64) -> f64,返回矩形面积。 - 写函数
fn is_even(n: i32) -> bool,不用return,用最后一行表达式返回。 - 写一个返回
()的函数,内部用loop和break提前退出。
7.3 🔄 控制流
- 用
if表达式实现:若n > 0返回"positive",否则返回"non-positive",结果存到变量并打印。 - 用
loop+break实现:从 1 累加,直到和大于 100,break时带出当前和并打印。 - 用
for i in 1..=10打印 1 到 10;再用for (i, v) in [10,20,30].iter().enumerate()打印下标与值。
7.4 🎯 综合
- 温度转换 :实现
fn celsius_to_fahrenheit(c: f64) -> f64,并在main里对 0、100 调用并打印结果。
完成后可配合 Rustlings 的 variables、functions、if、primitive_types 巩固。
八、✅ 总结
- 变量与类型 :
let/let mut,标量(整型/浮点/布尔/字符)+ 元组与固定长度数组;默认不可变、强类型;遮蔽在内层作用域结束后恢复外层绑定。 - 函数 :
fn 名(参数: 类型) -> 返回类型,最后一行的表达式(无分号)即返回值。 - 控制流 :
if(可作表达式)、loop(可break带值)、while、for x in 迭代器;条件必须是bool。 - 与 C++:默认不可变、无隐式类型当条件、数组固定长度、用表达式返回值。
- 下一步 :掌握这些后,进入所有权、借用与生命周期(阶段 2),再配合 Rustlings 与小项目巩固。
基础语法是 Rust 的入口,习惯「默认不可变 + 表达式即值」后,后面所有权与错误处理会顺很多。建议每天写几段小代码,把本教程里的示例和练习都敲一遍。
📦 变量类型
⚙️ 函数
🔄 控制流
📖 所有权
🦀 进阶
下一篇:正在编写...