原生数据类型
Rust 是静态类型语言,编译时就必须知道所有变量的类型。根据值及其使用方式,Rust 编译器通常能自动推导出变量的类型。
Rust 有两种原生数据类型: 标量(scalar)类型和复合(compound)类型。
除了标量类型(如 i32、f64、char、bool)和复合类型(元组、数组)这些原生类型外,复杂的数据结构几乎都由 struct 和 enum 构成。
标量类型
整型
语法: u/i + 位数
let a: u8 = 1;
let b: i32 = -2;u无符号整数: 范围0到2^n - 1i有符号整数: 范围-2^(n-1)到2^(n-1) - 1
| 无符号整数 | 有符号整数 | 长度 | 范围 |
|---|---|---|---|
u8 | i8 | 8-bit | u8:0-255 i8:-128-127 |
u16 | i16 | 16-bit | u16:0-65,535 i16:-32,768-32,767 |
u32 | i32(默认) | 32-bit | u32:0-约 42.9 亿 i32:约 -21.4 亿-21.4 亿 |
u64 | i64 | 64-bit | u64:0-1.84 x 10^19 i64:-9.22 x 10^18-9.22 x 10^18 |
u128 | i128 | 128-bit | |
usize | isize | 架构相关 |
u代表Unsigned,特指Unsigned Integer(无符号整数),只能表示非负数(0 和正数)。i代表Integer,特指Signed Integer(有符号整数),可以表示正数、负数和零。- usize/isize 的大小取决于目标平台的指针宽度: 32位系统为4字节(i32/u32),64位系统为8字节(i64/u64)。
1.整型字面值
0b 表示二进制、0o 表示八进制、0x 表示十六进制,默认是十进制。
| 数字字面值 | 例子 |
|---|---|
Binary (二进制) | let d: i32 = 0b1111_0000; |
Octal (八进制) | let c: i32 = 0o77; |
Decimal (十进制) | let a: i32 = 98_222; |
Hex (十六进制) | let b: i32 = 0xff; |
Byte (单字节字符,仅限于u8) | let e: u8 = b'A'; |
2._ 分隔数字
使用 _ 分隔数字可以提高可读性,编译器会忽略它们:
let million = 1_000_000; // 等价于 let million = 1000000;
let hex = 0xFF_FF; // 等价于 let hex = 0xFFFF;3.字面量后缀类型标注
let a = 10u8; // 等于 let a: u8 = 10;
let b = 20i64; // 等于 let b: i64 = 20;
let c = 3.14_f32; // 等于 let c: f32 = 3.14;
let d = 30_isize; // 等于 let d: isize = 30;4.整数溢出
每种数值类型都有所能存储的最大数值和最小数值。当超出类型的有效范围时,Rust将报错(panic)。
let x: u8 = 255; // x = 255 是 u8 的最大值
let y = x + 1; // ❌ Debug 模式: panic!("attempt to add with overflow")Debug模式 会 panic; Release模式 会变成 0
浮点型
语法: f + 位数
let a: f32 = 3.14;
let b: f64 = 2.71828;| 类型 | 长度 | 精度 | 范围 |
|---|---|---|---|
f32 | 32-bit 单精度浮点数 | 约 6 ~ 9 位 | -1.18*10^38-3.40*10^38 |
f64(默认) | 64-bit 双精度浮点数 | 约 15 ~ 17 位 | -2.23*10^308-1.79*10^308 |
f32: 占用 32 位(4 字节)。f64: 占用 64 位(8 字节),正好是f32的两倍。
如果你计算圆周率,
f32可能只能精确到 3.141592,而f64能到 3.141592653589793
布尔类型 bool
true和false是仅有的两个值- 占 1 字节(8 bit)
let t = true;
let f: bool = false;
if true { } // ✅
if 1 { } // ❌ 不允许,整数不能作为布尔条件;Rust 不会将整数隐式转换为 bool
if 1 != 0 { } // ✅ 需要显式比较- 布尔值可以通过比较运算符(
==、<、>等)生成 - 布尔值可以通过逻辑运算符(
&&、||、!)组合
let a = 5;
let b = a > 3 && a < 10; // b = true字符类型 char
char 类型是Rust中最原始的字符类型
- 是一个
Unicode标量值(Unicode scalar value) - 占 4 字节(32 bit)
- 使用单引号
''声明
let z: char = 'ℤ'; // 使用显式类型注释
let a = 'A'; // 1 字节表示,但占 4 字节内存
let emoji = '😻'; // 也是 4 字节复合类型
复合类型是将多个不同类型的值组合成一个类型的主要方式
Rust 有两个原生的复合类型: 元组(tuple) 和 数组(array)
元组 tuple
- 定义语法:
let 变量 = (类型1, 类型2, ...) - 取值语法:
变量.索引或 模式匹配
let tup: (i32, f64, u8) = (500, 6.4, 1); // 元组类型注释
let (x, y, z) = tup; // 模式匹配取值
let first = tup.0; // 点号(.)取值
let second = tup.3; // ❌ 索引越界,编译错误
let mut tup1 = (1, 2, 3); // 定义一个可变元组
tup1.0 = 10; // ✅ 可以改变元组值| 特性 | 说明 |
|---|---|
| 大小 | 一旦定义,元组的长度是固定的 |
| 类型 | 元组中的每个元素可以是不同类型 |
| 所有权 | 元组每个元素的所有权是独立的 |
本质是一个没有名字的匿名结构体
空元组
空元组也称为"单元类型"(unit type),使用 () 表示,等价于 JavaScript 的 undefined,很多表达式和函数默认返回它。
let unit: () = ();数组 array
- 定义语法:
let 变量 = [类型; 长度] - 取值语法:
变量[索引]或 模式匹配
let a = [1, 2, 3, 4, 5]; // 类型和长度由编译器推导
let a: [i32; 5] = [1, 2, 3, 4, 5]; // 显式类型注释,长度必须与元素数量匹配
let a = [3; 5]; // 等于 let a = [3, 3, 3, 3, 3];
let first = a[0]; // [] 取值
let second = a[5]; // ❌ 索引越界,编译错误
let mut a = [1, 2, 3]; // 定义一个可变数组
a[0] = 10; // ✅ 可以改变元素值| 特性 | 说明 |
|---|---|
| 大小 | 一旦定义,数组长度是固定的 |
| 类型 | 数组中的每个元素的类型必须相同 |
| 所有权 | 数组的所有权是整体的 |
usize 和 isize
usize 和 isize 是 Rust 中的特殊整数类型,在 内存和指针 章节中会再介绍。
usize 和 isize 的大小取决于目标平台的指针宽度:
- 32位系统为4字节, 即
i32和u32 - 64位系统为8字节, 即
i64和u64
使用场景:
usize是数组索引和集合长度的标准类型(len()、[]索引均返回/接受usize)。isize常用于指针偏移量计算(负偏移时派上用场)。
as 基本类型转换
Rust 不会自动进行类型转换,需要使用 as 关键字显式转换。截断和溢出行为由目标类型决定:
// 数值类型转换
let a = 42i32 as f64; // i32 → f64
let b = 3.99f64 as i32; // f64 → i32,截断小数部分 → 3(不四舍五入)
let c = 300u32 as u8; // 超出范围时截断(300 % 256 = 44)
// bool类型转换
let d = true as u8; // bool → u8, true → 1, false → 0
let e = 0u8 as bool; // u8 → bool, 0 → false, 非0 → true
// char类型相关转换
let f = 'A' as u32; // char → u32(Unicode 码位: 65)
let g = 65u8 as char; // u8 → char → 'A'
as只能用于基本数值类型和char/指针之间的转换,不能用于String/&str等复杂类型(需用.to_string()/.parse()等方法)。
整数溢出的安全处理方法
除了 Debug 模式 panic / Release 模式截断外,标准库提供了四组安全方法:
| 方法 | 行为描述 | 适用场景 |
|---|---|---|
checked_add() | 安全加法,溢出时返回 None | 需要明确处理溢出情况 |
saturating_add() | 饱和加法,溢出时结果固定在该类型的最大值或最小值 | 需要边界保护 |
wrapping_add() | 环绕加法,溢出时按二进制环绕处理,行为与 Release 模式一致 | 需要循环计数 |
overflowing_add() | 返回元组 (result, is_overflow),同时获得结果和溢出标志 | 需要记录溢出事件 |
使用建议: 根据具体需求选择合适的方法,优先考虑 checked_add() 以获得更好的错误处理能力。
let x: u8 = 255;
x.checked_add(1) // → None(溢出返回 None)
x.saturating_add(1) // → 255(饱和: 钉在最大/最小值)
x.wrapping_add(1) // → 0(环绕: 和 Release 行为一致)
x.overflowing_add(1) // → (0, true)(返回结果和是否溢出的元组)