趣味学Rust基础篇(数据类型)
作为一个名长期从事编码工作的码农,我一直觉得编写程序就想玩乐高一样,想象一下,你要用乐高积木搭建一个复杂的模型。每一块积木都有不同的形状和功能:有小小的 1x1 方块(基础积木),也有能组合多个小块的复杂组件(组合积木)。在 Rust 世界里,数据类型就是这些“积木”。我们要学习的,就是认识这些积木(数据类型),并用它们构建出强大的程序。
Rust 是一个静态类型语言,这就意味着它像一个非常严格的建筑师,必须在“开工”(编译)之前就搞清楚每一块积木的型号。不过,Rust 很聪明,很多时候它能根据你放进去的“零件”(值)自动猜出积木型号。
积木的两大类:标量(单一积木)和复合(组合积木)
Rust 的积木主要分两大类:
- 标量类型 (Scalar):代表一个独立的值,就像一个 1x1 的小方块。
- ** 复合类型 **(Compound):能把多个值组合在一起,就像一个能连接多个小方块的大组件。
第一类:标量积木(单一值)
标量积木有四种基本款:整数、浮点数、布尔值、字符。
1. 整数积木(i32
, u8
, isize
…)
整数就是没有小数点的数字,比如 42
, -100
。
-
有符号 vs 无符号:
i
开头的(如i32
):有符号(signed),能存正数、负数和零。i
代表 “integer”。u
开头的(如u8
):无符号(unsigned),只能存零和正数。u
代表 “unsigned”。
-
位数:后面的数字(如
32
)代表这块积木的大小(位数),决定了它能存多大的数字。i8
:8位,范围是 -128 到 127。u8
:8位,范围是 0 到 255。i32
:32位,范围巨大,从 -2,147,483,648 到 2,147,483,647。
举例说明:
let age: u8 = 25; // 年龄不可能是负数,用 u8 足够了
let temperature: i32 = -10; // 温度可以是零下,用 i32
let default_int = 42; // 不写类型,Rust 默认是 i32
isize
和usize
:这两个比较特殊,它们的大小取决于你的电脑是 32 位还是 64 位。它们通常用于数组索引或内存地址。
整数溢出:我们可以想象一个 u8
盒子(最大装 255),你非要塞进去 256。
- Debug 模式(开发时):Rust 会立刻大喊“Panic!”(崩溃),提醒你出错了。
- Release 模式(发布时):为了性能,Rust 会默默让它“回绕”:256 变成 0,257 变成 1… 这通常不是你想要的!
安全处理溢出:Rust 提供了专门的“安全工具”:
let max_u8: u8 = 255;
let result = max_u8.checked_add(1); // 检查加法是否会溢出
match result {Some(value) => println!("结果是 {value}"),None => println!("加法溢出啦!"),
}
- 整数写法:Rust 支持多种写法,还允许用
_
当“千分位”分隔符,超贴心!let decimal = 98_222; // 十进制,就是 98222 let hex = 0xff; // 十六进制,255 let octal = 0o77; // 八进制,63 let binary = 0b1111_0000; // 二进制,240 let byte = b'A'; // 字节 (u8),65 (ASCII 'A')
2. 浮点数积木(f32
, f64
)
浮点数就是带小数点的数字,比如 3.14
, -0.001
。
f32
:32位,单精度。f64
:64位,双精度。默认类型是f64
,因为现代 CPU 上它和f32
差不多快,但精度更高。
例子:
let pi = 3.1415926; // 不写类型,Rust 默认是 f64
let small_number: f32 = 1e-3; // 科学计数法,0.001
数学运算:
fn main() {let sum = 5 + 10; // 加法: 15let diff = 95.5 - 4.3; // 减法: 91.2let prod = 4 * 30; // 乘法: 120let quot = 56.7 / 32.2; // 除法: 约 1.76let trunc = -5 / 3; // 整数除法向零截断: -1 (不是 -1.666...)let rem = 43 % 5; // 求余: 3println!("trunc = {trunc}, rem = {rem}");
}
3. 布尔积木(bool
)
最简单的积木,只有两个状态:true
(真)和 false
(假)。大小是 1 个字节。
例子:
let is_raining = true;
let is_sunny: bool = false; // 显式类型注解
let comparison = 10 > 5; // 比较表达式返回 bool: true
布尔值主要用于 if
条件判断。
4. 字符积木(char
)
char
是 Rust 中最基础的“字母”类型。注意:它和字符串 String
不一样!
- 用单引号
' '
包裹。 - 大小是 4 个字节,可以表示Unicode 标量值,所以不仅能表示英文、数字,还能表示中文、日文、表情符号(emoji)等!
例子:
let letter = 'z';
let number = '7';
let heart = '❤';
let cat = '😻';
let chinese = '中';
let math_symbol = 'ℤ'; // 整数集符号
重要提示:一个 char
不一定对应人类认知的一个“字符”。例如,一个带重音符号的字母 é
可能由多个 Unicode 码位组成。对于文本,通常使用 String
类型。
第二类:复合积木(组合值)
复合类型能把多个标量积木组合成一个更复杂的组件。
1. 元组积木(tuple
)
元组就像一个异构的、固定大小的盒子,可以装不同类型的东西。
- 用小括号
( )
创建。 - 一旦创建,大小不能改变。
- 里面的元素可以是不同类型。
例子:
// 创建一个元组,包含整数、浮点数和字符
let tup: (i32, f64, char) = (500, 6.4, 'z'); // 可以加类型注解// 创建元组(不加类型注解,Rust也能推断)
let tup = (500, 6.4, 'z');// 方法一:解构(Destructuring) - 把大盒子拆成小盒子
let (x, y, z) = tup; // x=500, y=6.4, z='z'
println!("y 的值是:{y}");// 方法二:通过索引访问 - 直接从大盒子里拿东西
let x = tup.0; // 第一个元素 (索引从0开始)
let y = tup.1; // 第二个元素
let z = tup.2; // 第三个元素
println!("x={x}, y={y}, z={z}");
特殊元组:单元类型 ()
- 一个空的元组
()
,读作 “unit”。 - 它既是值也是类型。
- 当一个函数不返回任何有意义的值时,它会隐式返回
()
。fn do_something() {println!("我做了点事!");// 没有 return 语句,函数隐式返回 () }
2. 数组积木(array
)
数组就像一个同质的、固定大小的盒子,所有格子必须装相同类型的东西。
- 用方括号
[ ]
创建。 - 所有元素类型必须相同。
- 大小固定,创建后不能改变。
- 数据通常分配在栈上,访问非常快。
例子:
// 创建数组
let a = [1, 2, 3, 4, 5]; // 类型是 [i32; 5]// 显式类型注解
let a: [i32; 5] = [1, 2, 3, 4, 5];// 用同一个值初始化所有元素
let a = [3; 5]; // 等同于 [3, 3, 3, 3, 3]// 访问数组元素(通过索引)
let first = a[0]; // 第一个元素
let second = a[1]; // 第二个元素
println!("第一个元素是:{first}");
**数组 vs 向量 **(Vec)
- 数组:大小固定,栈上分配,性能极高。适合大小已知且不变的场景,比如一周的天数、一年的月份。
let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; // 12个月,固定!
- **向量 **(Vec):大小可变,堆上分配。当你不确定需要多少元素,或者元素会增减时,用
Vec
。这是标准库提供的类型。
数组越界:Rust 的“安全护栏”
这是 Rust 内存安全的核心体现!尝试访问数组不存在的索引会怎样?
use std::io;fn main() {let a = [1, 2, 3, 4, 5];println!("请输入一个索引 (0-4):");let mut input = String::new();io::stdin().read_line(&mut input).expect("读取失败");let index: usize = input.trim().parse().expect("请输入数字");let element = a[index]; // ⚠️危险操作!如果 index >= 5 会怎样?println!("元素是:{element}");
}
- 结果:如果你输入
10
,程序不会偷偷访问内存垃圾,而是会立刻 panic(崩溃),并清晰地告诉你:
thread ‘main’ panicked at ‘index out of bounds: the len is 5 but the index is 10’ - 为什么这很重要?在 C/C++ 等语言中,这种越界访问可能导致程序读取或写入非法内存,造成安全漏洞(如缓冲区溢出攻击)。Rust 通过这种“快速失败”机制,在运行时保护你,确保内存访问是安全的。
总结:Rust 数据类型的“设计哲学”
通过学习这些数据类型,我们可以看到 Rust 的核心思想:
- 明确性与安全性:类型必须清晰(静态类型),默认不可变,访问越界会 panic。这些规则在编译时和运行时帮你抓住错误。
- 性能与控制:提供了从
u8
到i128
的精细整数选择,isize/usize
用于系统编程,数组在栈上分配以获得极致性能。 - 现代性:
char
支持 Unicode,让程序天生支持多语言。 - 组合性:通过元组和数组,可以轻松组合数据。而
Vec
等标准库类型则提供了更高级的抽象。 - 零成本抽象:这些类型和检查带来的安全性,几乎没有运行时性能损失。
现在,你已经掌握了 Rust 构建程序的“基本积木”。记住,选择正确的“积木”(数据类型)是写出高效、安全、可维护代码的第一步!