数据类型
约 2057 字大约 7 分钟
2026-02-04
适合谁:刚开始学 Rust,觉得官方介绍分散、概念多、记不住。 你会获得什么:一张“类型地图” + 一套“选型规则” + 一堆“可复制运行的小例子” + 常见坑的避坑手册。 学完你应该能:看懂变量类型、写对字面量、知道何时用
i32/usize、理解溢出/越界为何会 panic,并用标准库方法显式处理。
目录(建议按顺序读)
- 先把“类型地图”装进脑子
- 整数:范围、字面量、怎么选类型
- 整数溢出:debug vs release,4 组官方处理方法
- 浮点:
f32/f64与“为什么会不精确” - 布尔:
bool的真实用法 - 字符:
char不是 1 字节,也不是字符串 - 元组:一包不同类型的值,怎么拆、怎么取
- 数组:固定长度的连续内存,为什么会越界 panic
- 数组 vs Vec:什么时候该换 Vec
- 选型速查表 + 练习清单
1) 先把“类型地图”装进脑子
Rust 的基础数据类型分两大类:
A. 标量类型(Scalar Types)——一次一个值
- 整数:
i* / u* / isize / usize - 浮点:
f32 / f64 - 布尔:
bool - 字符:
char
B. 复合类型(Compound Types)——多个值组合成一个值
- 元组:
(T1, T2, ...) - 数组:
[T; N]
本章最重要的一句话: Rust 是静态类型语言,编译器必须在编译期知道类型。大多数情况下能推断;推断不了就需要你写
: 类型。
2) 整数:范围、字面量、怎么选类型
2.1 i 和 u:负数能不能存在
i开头:有符号整数(可以为负),如i32u开头:无符号整数(永远非负),如u32
Rust 的整数都是固定宽度:8/16/32/64/128 位,还有跟平台相关的 isize/usize。
| 位宽 | 有符号 | 无符号 |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| 依赖架构 | isize | usize |
2.2 范围怎么理解(不用死背)
你只要记住一个规律:
- 有符号:
-(2^(n-1)) ..= 2^(n-1)-1 - 无符号:
0 ..= 2^n - 1
例如:
i8:-128..=127u8:0..=255
小贴士:如果你想在代码里直接用最大/最小值,不必背范围:
i32::MAX、i32::MIN、u8::MAX等都可以直接用。
2.3 isize/usize:为什么到处都是 usize
你会在“索引”和“长度”里频繁看到 usize:
len()返回usize- 数组/切片索引需要
usize - 因为
usize与平台指针宽度一致(64 位机器通常是 64 位)
初学者选型规则(非常实用):
- 不确定就用
i32(Rust 默认整数类型就是i32)- 只要是索引/长度(
len()、数组下标、切片范围)就用usize- 明确只会非负且可能很大(计数、容量)可以考虑
u32/u64
2.4 整数字面量:写法越多,越容易读懂别人的代码
| 写法 | 示例 |
|---|---|
| 十进制 | 98_222 |
| 十六进制 | 0xff |
| 八进制 | 0o77 |
| 二进制 | 0b1111_0000 |
字节(仅 u8) | b'A' |
_只是视觉分隔符:1_000=1000- 字面量可以加类型后缀:
57u8、10i64
可运行小例子:
fn main() {
let a = 10; // 默认 i32
let b = 10u8; // u8
let c: i64 = 10; // i64
let hex = 0xff; // 默认 i32
let bin = 0b1010_0001;
let byte = b'A'; // u8
println!("{a} {b} {c} {hex} {bin} {byte}");
}3) 整数溢出:debug vs release,4 组官方处理方法
你很快会遇到一个震撼新手的问题:同样的代码,debug 会 panic,release 反而不 panic?
3.1 两种编译模式的默认行为
- debug(默认
cargo run) 溢出检查更严格,溢出会 panic - release(
cargo run --release) 默认不做会 panic 的溢出检查,溢出会 回绕(wrapping)
比如 u8 最大是 255:
- 256 在回绕模式下会变成 0
- 257 变成 1
官方态度:依赖回绕通常是错误。你应该“显式选择”你想要的行为。
3.2 标准库给你的 4 种“显式”策略(强烈建议掌握)
对整数原始类型,有这些方法家族:
wrapping_*:总是回绕checked_*:溢出返回Noneoverflowing_*:返回(值, 是否溢出)saturating_*:溢出钳到最大/最小
对比示例:
fn main() {
let x: u8 = 250;
println!("wrapping_add: {}", x.wrapping_add(10));
println!("checked_add: {:?}", x.checked_add(10));
println!("overflowing_add: {:?}", x.overflowing_add(10));
println!("saturating_add: {}", x.saturating_add(10));
}你在写业务代码时,最常用的是
checked_*(安全计算)和saturating_*(比如 UI 进度条/百分比)。
4) 浮点:f32/f64 与“为什么会不精确”
4.1 两种浮点类型
f32:32 位f64:64 位(默认)
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
println!("{x} {y}");
}4.2 你必须知道:浮点是近似
浮点遵循 IEEE-754 表示法,很多十进制小数无法用二进制精确表示(比如 0.1)。 所以 0.1 + 0.2 不一定等于“肉眼认为的 0.3”。
新手建议:
- 金额、精确计数等场景不要直接用浮点;后续应学习定点/整数表示或专用库(这属于进阶主题)。
5) 布尔:bool 的真实用法
bool 只有 true/false,常用于条件控制:
fn main() {
let ok = true;
let fail: bool = false;
if ok && !fail {
println!("condition is true");
}
}记住:Rust 的
if是表达式(能产生值),但分支必须类型一致(这是很多初学者第一次被“类型系统教育”的地方)。
6) 字符:char 不是 1 字节,也不是字符串
6.1 char 的关键事实
- 用单引号:
'a' - 占 4 字节
- 表示一个 Unicode 标量值:能表示中文、emoji 等
fn main() {
let a = 'z';
let b: char = 'ℤ';
let c = 'c';
let d = '中';
println!("{a} {b} {c} {d}");
}重要提醒:
char≠ “人类直觉里的一个字形”。有些“看起来一个字符”的东西可能由多个 Unicode 单元组成。 字符串与 UTF-8 会在后续章节系统讲。
7) 元组:一包不同类型的值(怎么拆、怎么取)
元组适合“把几个相关但类型不同的值放一起”,长度固定。
7.1 创建与标注
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
println!("{:?}", tup);
}7.2 解构(destructuring)
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("x={x}, y={y}, z={z}");
}7.3 点号取值(按位置)
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
println!("{} {} {}", x.0, x.1, x.2);
}7.4 单元类型 ()
()既是一个值,也是一个类型- 常见:函数不返回有意义的值时,返回
() - 许多语句块在没有返回其他值时会“隐式产生
()”
8) 数组:固定长度的连续内存(为什么会越界 panic)
数组是 固定长度、元素同类型 的集合。
8.1 创建数组
fn main() {
let a = [1, 2, 3, 4, 5];
println!("{:?}", a);
}8.2 写出类型 [T; N]
fn main() {
let a: [i32; 5] = [1, 2, 3, 4, 5];
println!("{:?}", a);
}8.3 快速重复初始化 [value; len]
fn main() {
let a = [3; 5]; // [3,3,3,3,3]
println!("{:?}", a);
}8.4 索引访问与越界
fn main() {
let a = [10, 20, 30, 40, 50];
let first = a[0];
let second = a[1];
println!("{first} {second}");
}为什么越界会 panic?
当你写 a[index],Rust 会在运行时检查 index < a.len(),越界就 panic。 这是为了安全:防止访问非法内存。
8.5 更“博客推荐”的安全写法:用 get
get 返回 Option:
Some(&value):合法None:越界
fn main() {
let a = [1, 2, 3, 4, 5];
let index: usize = 10;
match a.get(index) {
Some(v) => println!("value = {v}"),
None => println!("index out of bounds"),
}
}新手非常推荐:这能让你自然练到
Option + match,写出不会崩溃的程序。
9) 数组 vs Vec:什么时候该换 Vec?
官方的直觉很简单:
- 数组
[T; N]:元素数量固定(比如 12 个月) - 向量
Vec<T>:需要增长/缩小(绝大多数集合场景)
初学者一句话:不确定就用 Vec。数组更像“固定配置”。
10) 选型速查(记住这几条,少走 80% 弯路)
10.1 整数默认选 i32
- 你没写类型后缀/标注时,Rust 默认整数推断为
i32 - 大多数业务场景
i32足够
10.2 索引/长度用 usize
len()→usize- 下标
a[index]→index: usize - 切片范围
&a[0..n]→n: usize
10.3 u8 常见于字节
b'A'是u8- 网络/文件/编码场景常用
u8
10.4 char 不是字符串
- 一个
char是 4 字节的 Unicode 标量值 - 字符串是
&str/String(后面章节)
贡献者
flycodeu
版权所有
版权归属:flycodeu
