📚 目录导航
- 什么是 Rust 的类型系统?
- 标量类型(整数、浮点、布尔、字符)
- 复合类型(元组、数组)
- 字符串类型:
&str
与String
- 自定义类型(结构体和枚举)
- 零大小类型(ZST):不占空间却意义重大
_
占位写法:只关心类型不关心名字- 底类型
!
:不会返回的类型到底有什么用? - 总结 + 类型速查表
1️⃣ 什么是 Rust 的类型系统?
Rust 是一门:
- 静态类型语言:变量类型在编译时就必须确定。
- 强类型语言:类型不允许自动转换。
let x = 10; // 推导为 i32
let y: u8 = x; // ❌ 错误:不能自动转换
let y: u8 = x as u8; // ✅ 正确方式
这样的设计让程序在“还没跑起来”就发现 bug,提高了可靠性和安全性。
2️⃣ 标量类型:最基本的类型单位
类型 | 示例 | 说明 |
---|---|---|
整数 | let a: i32 = 42; | 默认类型是 i32 |
浮点数 | let b = 3.14; | 默认类型是 f64 |
布尔值 | let f = true; | 只能是 true 或 false |
字符 | let c = '🚀'; | 单个 Unicode 字符 |
3️⃣ 复合类型:多个值组合成一个
✅ 元组 tuple
let info = ("Tom", 18, true);
let (name, age, active) = info;
✅ 数组 array
let scores = [90, 80, 70];
let first = scores[0];
数组长度固定,元素类型必须一致。
4️⃣ 字符串类型:&str 与 String 的区别
类型 | 可变 | 分配位置 | 示例 | 类比 |
---|---|---|---|---|
&str | ❌ | 栈/静态区 | "Tom" | 公告栏只读纸条 |
String | ✅ | 堆上 | String::from("Tom") | 自己的记事本 |
let a = "Tom"; // &str
let b = String::from("Tom"); // String,可变
5️⃣ 自定义类型:结构体和枚举
✅ 结构体 struct
struct User {
name: String,
age: u8,
}
✅ 枚举 enum
enum State {
Loading,
Success(String),
Error(String),
}
6️⃣ 零大小类型(ZST):不占空间却意义重大
❓ 什么是 ZST?
ZST(Zero-Sized Type)就是占用内存为 0 字节的类型。
use std::mem::size_of;
println!("{}", size_of::<()>()); // 输出:0
✅ 常见 ZST 类型与用途
类型 | 示例 | 用途 |
---|---|---|
单元类型 | () | 没有返回值或表示“空” |
空数组 | [u8; 0] | 表示空集合,不占内存 |
空结构体 | struct Marker; | 类型标记,泛型占位 |
空元组结构体 | struct Wrapper(); | 同上 |
PhantomData<T> | PhantomData::<u32> | 声明与某类型有关,不持有它 |
🧠 为什么有 ZST 这种设计?
虽然它们不占空间,但可以在“类型层面”起作用:
- 编译期进行类型区分(比如泛型中标记数据类型)。
- 触发行为但不带数据(比如用 Marker 调用函数)。
- 用于实现某些 trait 的结构不需要状态。
✅ 例子 1:类型标记
struct Marker;
fn init(_: Marker) {
println!("执行初始化操作");
}
init(Marker); // ✅ Marker 不占空间但可以控制逻辑分支
✅ 例子 2:PhantomData 用法详解
use std::marker::PhantomData;
struct MyVec<T> {
_marker: PhantomData<T>, // 虽然不存 T,但逻辑上依赖 T
}
❓ 为什么用它?
- 在泛型结构体中,如果你没有真正存储
T
,Rust 会认为这个结构体和T
没有关系,可能会在生命周期检查、Drop 顺序上出问题。 PhantomData<T>
告诉编译器:“这个结构体跟 T 有关系”,即使我不持有 T 的值。
✅ 例子 3:ZST 特性 - 可重复但无代价
let a = ();
let b = ();
assert_eq!(std::ptr::addr_of!(a), std::ptr::addr_of!(b)); // 地址相同!
7️⃣ _
占位写法:不关心名字但关心类型
fn consume(_: String) {
println!("调用了函数,但不需要参数内容");
}
_
表示“我不打算用这个值的名字”,但类型仍然必须匹配。
8️⃣ 底类型 !
:不会返回的类型到底有什么用?
❓ 什么是底类型 !
?
Rust 中的 !
代表“永不返回”的类型,叫 bottom type。
fn never_return() -> ! {
panic!("程序终止");
}
✅ 为什么需要 !
类型?
!
类型可以出现在任何位置,因为它永远不会产生值。- 编译器会认为它“能转成任何类型”,就像数学中空集是任何集合的子集。
✅ 典型使用场景
场景 | 示例 | 用途说明 |
---|---|---|
程序崩溃 | panic!("错误!") | 程序终止,不再返回 |
死循环 | loop {} | 永远运行,永不返回 |
编译期保证 | unreachable!() | 明确告诉编译器:这里不会执行 |
match 分支 | match x { _ => unreachable!() } | 用于逻辑保证无误的防御性编程 |
✅ 例子:panic! 与 ! 类型推断
fn get_name(fail: bool) -> String {
if fail {
panic!("失败"); // 返回类型是 `!`
}
String::from("Tom")
}
虽然两个分支的返回类型不同,但 Rust 会自动推断:!
可以当作任意类型处理,因此允许通过编译。
9️⃣ 总结 + 类型速查表
类型 | 分类 | 示例 | 说明 |
---|---|---|---|
i32 , u8 | 整数 | let a = 10; | 默认整数是 i32 |
f64 , f32 | 浮点数 | let pi = 3.14; | 默认浮点是 f64 |
bool | 布尔 | let f = true; | 条件判断 |
char | 字符 | let c = '🚀'; | Unicode 字符 |
&str , String | 字符串 | "Tom" 、String::from("Tom") | 静态与堆上字符串 |
() | ZST | let x: () = (); | 单元类型 |
[T; 0] | ZST | let a: [u8; 0] = []; | 空数组 |
struct Marker; | ZST | let m = Marker; | 类型标记器 |
PhantomData<T> | ZST | PhantomData::<T> | 类型关联声明 |
_ | 占位写法 | fn f(_: T) | 忽略参数名 |
! | 底类型 | panic!() 、loop {} | 不会返回,终结流程分支 |