适用读者:零基础学习者。
核心目标:理解代码的基本组织单位,掌握变量、基础数据类型、判断分支与重复逻辑,能够阅读并编写简单的指令流。
1. 人生第一行代码与程序骨架解剖
打开任意 Rust 程序,最经典的起点莫过于打印一行文字:
fn main() {
println!("Hello, world!");
}初学者可能会觉得这几个单词和标点符号古怪突兀。让我们将其彻底拆解为直观的日常概念:
1.1 函数(fn):专能的加工机器
fn是英文 function(函数)的缩写。- 在数学中,函数是 ;但在计算机的世界里,函数是一台封装好的专用自动化加工机器。
- 这台机器有明确的名称、接收指定的原材料(参数)、按照内部规定的流水线步骤操作,并最终产出加工成品(返回值)。
1.2 main():整座工厂的总电源开关
- 计算机在运行你的程序时,内存里可能存放着成千上万行代码。计算机从哪里开始执行第一步?
- 操作系统约定:所有独立运行的 Rust 二进制程序,必须且只能有一个名为
main的函数。当程序启动时,CPU 就会自动把指针定位到main函数的第一行代码。
1.3 花括号 {}:设立结界(作用域)
{与}就像是一堵围墙,它们框定出的封闭空间在计算机专业中被称为作用域(Scope,又称代码块)。- 只有写在这堵围墙内部的指令,才属于该函数负责执行的流程;围墙之内产生的临时垃圾,走出围墙后也会被销毁。
1.4 println!:呼叫控制台信使
- 它负责将括号中的文本或数值打印输出到你的电脑终端屏幕上。
- 为什么名字末尾带着一个惊叹号
!?
在 Rust 中,末尾带 ! 的不是普通函数,而是宏(Macro)。
- 普通函数要求参数的个数和类型必须提前定死(例如规定只能传 2 个数字);
- 宏则像是一个“元代码生成器”,它能在编译阶段自动根据你传入的内容展开代码,允许接收任意数量、任意类型的参数并校验格式安全。
1.5 分号 ;:步骤终结的句号
- 每一条执行指令的结尾都必须跟上分号
;。 - 它就像人类写信时的句号,明确宣告:“本项操作已吩咐完毕,请计算机准备执行下一条指令”。
2. 变量与存储:内存中的“贴标签盒子”模型
程序光有指令是不够的,它必须记录并随时改变数据。这种用来暂存数据的命名载体,在编程中被称为变量(Variable)。
2.1 盒子模型与赋值的真相
let x = 5;这一行代码在计算机底层发生了什么?
- 申请空间:
let关键字告诉计算机:“请在内存工作台(RAM)上为我圈出一块空地,放一个盒子”。 - 命名贴签:将这个盒子命名为
x,以便人类后续可以通过名字随时找到它。 - 注入数据:符号
=在编程中绝不是数学上的‘相等’!它被称为赋值运算符(Assignment Operator)。它的物理含义是:“将等号右边的值(5),单向灌入等号左边的盒子(x)中存放”。
┌───────────────┐
│ 内存地址 0x7F │
│ [ 5 ] │ <=== 变量标签名: x
└───────────────┘3. Rust 的核心性格:默认不可变性(Immutability)
如果你在代码中写下:
let x = 5;
x = 6; // 编译报错!无法对不可变变量进行二次赋值编译器会断然拒绝编译!为什么?
3.1 现实类比:被盖上公章的合同
在日常生活中,如果有人给你递来一份签署好的劳动合同,默认情况下你是绝对不能在上面乱涂乱改的。如果代码中成千上万个变量都可以随时被任何角落的代码悄悄改动,程序稍大一点,你就再也无法推测某个数据此刻到底变成了什么,这就是无数严重 Bug 的根源。
Rust 坚持最严格的防呆设计:所有变量在创建时,默认盖上密封盖,永远是只读的。
3.2 显式开启可变权限:mut 关键字
如果你明确知道这个数值在后续业务中会发生变化(例如倒计时、游戏角色血量),你必须在声明时显式添加 mut(Mutable,可变的)关键字,如同撕开密封条:
let mut health = 100;
health = 90; // 合法!因为 health 被显式声明为 mut3.3 变量隐藏(Shadowing):换个新盒子
在同一个作用域中,Rust 允许你用 let 再次声明同名变量:
let spaces = " "; // spaces 此时装的是一串文本
let spaces = spaces.len(); // 计算长度:spaces 现在装的是数字 3!这并不违反不可变原则。它的物理实质是:旧的盒子被丢弃并遮蔽,新造了一个全新的盒子,恰好起了相同的名字。它允许你在转换数据形态时(如文本转数字),不用绞尽脑汁去发明临时变量名(如 spaces_str, spaces_num)。
4. 计算机的度量衡:数据类型图谱(Data Types)
正如日常生活中我们需要用升来量度液体、用克来称量重量一样,计算机必须明确每个盒子里装的是什么规格的数据。Rust 是一门静态强类型语言,这意味着所有数据在编译时必须拥有明确的类型。
4.1 标量类型(Scalar Types:装单一数值的基础容器)
4.1.1 整数(Integers:没有小数点的计数)
计算机里的整数分为两大阵营:
- 有符号整数(Signed,以
i开头,如i32):类似于温度计,可正可负,最高位拿来当正负号标识。 - 无符号整数(Unsigned,以
u开头,如u32):只能表示非负数(0 和正整数)。例如人的年龄、图书页码、内存大小,用无符号更精确且容量翻倍。
后面的数字(8, 16, 32, 64, 128)代表占用多少个比特的物理空间:
u8(8 个比特,1 字节):取值范围为 。i32(32 个比特,4 字节):Rust 的默认推断类型,范围约为正负 21 亿。usize/isize:大小取决于你的电脑硬件架构(在 64 位操作系统上占用 64 比特),专门用来给内存和集合做索引用。
警告:什么是数值溢出(Overflow)?
如果一个
u8盒子最大只能装 255,你硬要让它装 256,就会发生“溢出”。这就像汽车里程表到头后归零一样,在低级语言中会引发严重逻辑灾难。Rust 在开发模式下检测到溢出会直接停止程序报警,严防安全隐患。
4.1.2 浮点数(Floating-Point:带小数点的数字)
f64(默认,64 位双精度浮点数,高精度,现代 CPU 计算极快)。f32(32 位单精度浮点数)。
4.1.3 布尔值(Boolean:逻辑真假)
- 只有两个取值:
true(真) 与false(假)。 - 占用 1 字节内存,专门用于控制流程做出决策。
4.1.4 字符(Character)
- 用单引号包围:
let letter = 'A'; let emoji = '😊'; - 占用 4 个字节,基于全宇宙通用的 Unicode 编码,能无缝容纳世界上任何人类文字与表情符号。
4.2 复合类型(Compound Types:把多个盒子打包在一起)
4.2.1 元组(Tuple:不同物品的百宝箱)
将多个不同类型的值捆绑在一起,长度一旦确定不可更改:
let person: (i32, f64, char) = (25, 175.5, 'M');
// 通过索引点语法取出内部物品(从 0 开始计数)
let age = person.0; // 25
let height = person.1; // 175.54.2.2 数组(Array:规格完全一致的药丸排板)
在内存中必须紧密连续排布,每个元素的类型必须完全一致,且长度固定:
let scores = [98, 85, 100, 72, 90];
let first = scores[0]; // 获取第一个元素,值为 98边界检查安全防护:如果你试图读取 scores[10],Rust 在运行时会瞬间触发保护并停机报错,绝不允许读到未经许可的邻近内存,这是杜绝黑客“缓冲区越界读取”攻击的关键屏障。5. 控制流:计算机如何做决策与重复劳动
计算机之所以强大,不仅在于它能算,更在于它能根据条件自动做决定,并且不知疲倦地重复工作。
5.1 条件分支:if 表达式(人生的岔路口)
let temperature = 30;
if temperature > 28 {
println!("天气炎热,开启空调。");
} else {
println!("温度舒适。");
}- 核心规矩:
if后面的条件必须严格是布尔值(true或false)。在很多语言中写if 1可以蒙混过关,但在 Rust 中严禁此类隐式猜测。
5.2 循环:不知疲倦地重复(loop, while, for)
loop:无条件永久运转
除非在内部遇到 break(打破循环并退出)或 continue(跳过本次剩余步骤直接进入下一轮),否则会一直执行。
while:当条件满足时持续运转
let mut count = 3;
while count > 0 {
println!("倒计时: {}", count);
count -= 1;
}
println!("发射!");for in:最高效安全的清单遍历(黄金标准)
let fruits = ["苹果", "香蕉", "橙子"];
for fruit in fruits {
println!("水果篮里有: {}", fruit);
} 为什么推荐 for in?
它消除了手动维护下标索引(如 i = i + 1)的繁琐,在彻底根除“数组越界” Bug 的同时,由于编译器知道其确切上下界,底层机器码执行速度反而最快。
6. 核心认知破局:语句 vs. 表达式
这是初学者迈向成熟程序员最重要的一道心智门槛:
- 语句(Statement):执行了一项操作,但两手空空,不产生任何产出值。
例如:let x = 6; 是一条语句。它把 6 放进盒子里,但整句话本身没有值。因此你不能写 let a = (let b = 6);。
- 表达式(Expression):经过计算后,必定会“吐出”一个具体的结果值。
例如:5 + 5 会吐出 10;temperature > 28 会吐出 true。
6.1 代码块 {} 也是表达式
在 Rust 中,用花括号包裹的代码块本身就是一个表达式!它的最终结果等于这块代码里最后一行表达式的值:
let score = {
let base = 50;
let bonus = 20;
base + bonus // 注意!这里最后一行末尾千万不能加分号!
};
// 此时 score 的值被赋予为 706.2 分号 ; 的“价值吞噬魔法”
base + bonus是一个吐出 70 的表达式;- 一旦你在末尾加上分号变成了
base + bonus;,它就瞬间被降级为一条语句!它的计算结果被抛弃,最终只返回一个毫无意义的空单元类型()。 - 牢记:当你想让一个代码块或函数把算出的结果交还给外部时,最后一行绝不要加分号。