Rust学习教程2:基本语法

🦀 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 🔒 变量声明:letlet 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 = 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` | `[T; N]` 固定长度,越界 panic | | **函数返回** | 必须 `return` 或构造返回值 | 最后表达式无分号即返回值 | | **条件** | 可用整数、指针等 | 条件必须是 `bool` | | **循环** | `for(;;)` / `while` / 范围 for | `loop` / `while` / `for x in 迭代器` | | **无 return** | 仅 `void` 函数或单表达式函数 | 任意函数都可用末尾表达式返回值 | **思维差异**: * C++:默认可变、多种所有权( raw 指针/引用/智能指针)。 * Rust:默认不可变、单一所有权,后续会学到借用与生命周期。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/a9566e7780584a38882cbcb5a355b554.png) 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](https://play.rust-lang.org/) 或本地 `cargo new` 项目中完成。 #### 7.1 📦 变量与类型 1. 声明不可变变量 `pi: f64 = 3.14159`,再声明可变变量 `count: i32`,在循环中自增到 5 并打印。 2. 定义一个长度为 4 的数组,用 `for` 遍历并打印每个元素。 3. 定义一个元组 `(i32, bool, &str)`,分别用 `.0`、`.1`、`.2` 和解构两种方式取出值并打印。 4. **遮蔽与作用域** :在外层定义 `let x = 10`,在内层块中 `let x = x * 2` 并打印,离开内层后再打印 `x`,验证外层 x 仍为 10。 #### 7.2 ⚙️ 函数 5. 写函数 `fn area_rect(w: f64, h: f64) -> f64`,返回矩形面积。 6. 写函数 `fn is_even(n: i32) -> bool`,不用 `return`,用最后一行表达式返回。 7. 写一个返回 `()` 的函数,内部用 `loop` 和 `break` 提前退出。 #### 7.3 🔄 控制流 8. 用 `if` 表达式实现:若 `n > 0` 返回 `"positive"`,否则返回 `"non-positive"`,结果存到变量并打印。 9. 用 `loop` + `break` 实现:从 1 累加,直到和大于 100,`break` 时带出当前和并打印。 10. 用 `for i in 1..=10` 打印 1 到 10;再用 `for (i, v) in [10,20,30].iter().enumerate()` 打印下标与值。 #### 7.4 🎯 综合 11. **温度转换** :实现 `fn celsius_to_fahrenheit(c: f64) -> f64`,并在 `main` 里对 0、100 调用并打印结果。 完成后可配合 [Rustlings](https://github.com/rust-lang/rustlings/) 的 `variables`、`functions`、`if`、`primitive_types` 巩固。 *** ** * ** *** ### 八、✅ 总结 * **变量与类型** :`let` / `let mut`,标量(整型/浮点/布尔/字符)+ 元组与固定长度数组;默认不可变、强类型;遮蔽在内层作用域结束后恢复外层绑定。 * **函数** :`fn 名(参数: 类型) -> 返回类型`,最后一行的表达式(无分号)即返回值。 * **控制流** :`if`(可作表达式)、`loop`(可 `break` 带值)、`while`、`for x in 迭代器`;条件必须是 `bool`。 * **与 C++**:默认不可变、无隐式类型当条件、数组固定长度、用表达式返回值。 * **下一步** :掌握这些后,进入**所有权、借用与生命周期**(阶段 2),再配合 Rustlings 与小项目巩固。 基础语法是 Rust 的入口,习惯「默认不可变 + 表达式即值」后,后面所有权与错误处理会顺很多。建议每天写几段小代码,把本教程里的示例和练习都敲一遍。 📦 变量类型 ⚙️ 函数 🔄 控制流 📖 所有权 🦀 进阶 *** ** * ** *** 上一篇:[《Rust学习教程1:搭建Rust环境》](https://blog.csdn.net/qq_41898196/article/details/158127295) 下一篇:*正在编写...*

相关推荐
LYS_06182 小时前
c++学习(1)(编译过程)
c++·学习
键盘鼓手苏苏2 小时前
Flutter for OpenHarmony 实战:Envied — 环境变量与私钥安全守护者
开发语言·安全·flutter·华为·rust·harmonyos
特种加菲猫2 小时前
C++核心语法入门:从命名空间到nullptr的全面解析
开发语言·c++
坚持就完事了2 小时前
Java泛型
java·开发语言
Channing Lewis2 小时前
zoho crm的子表添加行时,有一个勾选字段,如何让它在details页面新建子表行(点击add row)时默认是勾选的
开发语言·前端·javascript
Miqiuha2 小时前
工作答辩框架
java·开发语言
happymaker06262 小时前
Java学习日记——DAY25(JavaSE完结)
java·开发语言·学习
qq_24218863323 小时前
快速搭建跨环境检测服务的步骤
linux·开发语言·windows·python·macos
专业开发者3 小时前
Wi-Fi 技术学习:面向 Wi-Fi 网络的抗干扰技术
网络·学习