数据类型
Rust 是静态类型(statically typed)语言,编译时必须知道所有变量的类型。
类型系统概览
Loading diagram...
标量类型
标量类型代表单个值。Rust 有四种主要的标量类型:
1. 整数类型
整数是没有小数部分的数字:
| 长度 | 有符号 | 无符号 | 范围(有符号) | 范围(无符号) |
|---|---|---|---|---|
| 8-bit | i8 | u8 | -128 ~ 127 | 0 ~ 255 |
| 16-bit | i16 | u16 | -32,768 ~ 32,767 | 0 ~ 65,535 |
| 32-bit | i32 | u32 | -2³¹ ~ 2³¹-1 | 0 ~ 2³²-1 |
| 64-bit | i64 | u64 | -2⁶³ ~ 2⁶³-1 | 0 ~ 2⁶⁴-1 |
| 128-bit | i128 | u128 | -2¹²⁷ ~ 2¹²⁷-1 | 0 ~ 2¹²⁸-1 |
| arch | isize | usize | 取决于架构 | 取决于架构 |
fn main() {
let a: i32 = -42; // 有符号 32 位
let b: u32 = 42; // 无符号 32 位
let c = 98_222; // 默认 i32
let d: isize = 100; // 平台相关(64位系统 = i64)
// 整数字面量
let decimal = 98_222; // 十进制
let hex = 0xff; // 十六进制
let octal = 0o77; // 八进制
let binary = 0b1111_0000; // 二进制
let byte = b'A'; // 字节(仅限 u8)
}
整数溢出:
fn main() {
let mut x: u8 = 255;
// x = x + 1; // Debug 模式:panic!
// Release 模式:溢出回绕(wrap around)为 0
// 显式处理溢出
let y = x.wrapping_add(1); // 256 → 0
let z = x.checked_add(1); // 返回 Option<u8>: None
let w = x.saturating_add(1); // 饱和为 255
let o = x.overflowing_add(1); // 返回 (值, 是否溢出)
}
2. 浮点类型
Rust 有两种浮点类型:f32(单精度)和 f64(双精度,默认):
fn main() {
let x = 2.0; // f64(默认)
let y: f32 = 3.0; // f32
// 浮点运算
let sum = 5.0 + 10.0;
let difference = 95.5 - 4.3;
let product = 4.0 * 30.0;
let quotient = 56.7 / 32.2;
// 特殊值
let inf = f64::INFINITY;
let neg_inf = f64::NEG_INFINITY;
let nan = f64::NAN;
println!("NaN == NaN? {}", nan == nan); // false!
}
⚠️ 注意:浮点数遵循 IEEE-754 标准,可能有精度问题。
3. 布尔类型
布尔类型只有两个值:true 和 false,占用 1 字节:
fn main() {
let t = true;
let f: bool = false;
// 常用于条件语句
if t {
println!("这是真的!");
}
// 布尔运算
let and = true && false; // false
let or = true || false; // true
let not = !true; // false
}
4. 字符类型
char 类型代表一个 Unicode 标量值,占用 4 字节:
fn main() {
let c = 'z';
let z: char = 'ℤ';
let heart_eyed_cat = '😻';
let chinese = '中';
println!("Size of char: {} bytes", std::mem::size_of::<char>()); // 4
// char 是 Unicode 标量值
let emoji = '🦀'; // Rust 的吉祥物 Ferris!
}
char vs 字符串:
fn main() {
let c: char = 'A'; // 单个 Unicode 字符,4 字节
let s: &str = "A"; // 字符串切片,1 字节(UTF-8 编码)
// char 可以包含任何 Unicode
let rustacean = '🦀'; // ✅ 合法
// let invalid = '🦀😻'; // ❌ 编译错误:char 只能是单个字符
}
复合类型
复合类型可以将多个值组合成一个类型。
1. 元组(Tuple)
元组可以组合不同类型的值:
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
// 解构(destructuring)
let (x, y, z) = tup;
println!("y 的值: {}", y); // 6.4
// 通过索引访问
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
// 单元类型(unit type)
let unit = (); // 空元组,大小为 0 字节
}
元组的内存布局:
fn main() {
let tuple: (u8, u16, u8) = (1, 2, 3);
println!("Size: {} bytes", std::mem::size_of_val(&tuple)); // 6 bytes(可能有填充)
}
2. 数组(Array)
数组中的所有元素必须是相同类型,且长度固定:
fn main() {
// 数组声明
let a = [1, 2, 3, 4, 5];
let b: [i32; 5] = [1, 2, 3, 4, 5]; // 类型标注:[类型; 长度]
// 初始化相同值
let c = [3; 5]; // [3, 3, 3, 3, 3]
// 访问元素
let first = a[0];
let second = a[1];
// 数组是栈分配的
println!("数组大小: {} bytes", std::mem::size_of_val(&a)); // 20 bytes (5 * 4)
}
数组 vs Vec:
fn main() {
// 数组:固定长度,栈分配
let arr = [1, 2, 3, 4, 5];
// arr.push(6); // ❌ 编译错误:数组不能改变长度
// Vec:动态长度,堆分配
let mut vec = vec![1, 2, 3, 4, 5];
vec.push(6); // ✅ 可以增长
}
越界访问:
fn main() {
let a = [1, 2, 3, 4, 5];
// let index = 10;
// let element = a[index]; // ⚠️ 运行时 panic!(而非编译错误)
// // thread 'main' panicked at 'index out of bounds'
// 安全访问
match a.get(10) {
Some(value) => println!("值: {}", value),
None => println!("索引越界"),
}
}
切片类型
切片(slice)是对连续序列的引用,没有所有权:
fn main() {
let a = [1, 2, 3, 4, 5];
// 切片
let slice: &[i32] = &a[1..3]; // [2, 3]
println!("切片: {:?}", slice);
// 字符串切片
let s = String::from("hello world");
let hello: &str = &s[0..5]; // "hello"
let world: &str = &s[6..11]; // "world"
}
类型转换
Rust 不会隐式转换数值类型:
fn main() {
let x: i32 = 10;
// let y: i64 = x; // ❌ 编译错误:类型不匹配
// 必须显式转换
let y: i64 = x as i64; // ✅ 使用 as 关键字
// 转换可能截断
let a: i32 = 1000;
let b: u8 = a as u8; // 232(溢出,取低 8 位)
println!("b = {}", b);
}
安全转换:
fn main() {
let x: i32 = 1000;
// 使用 try_into(需要 use std::convert::TryInto)
use std::convert::TryInto;
let y: Result<u8, _> = x.try_into();
match y {
Ok(val) => println!("转换成功: {}", val),
Err(_) => println!("转换失败:值超出范围"),
}
}
类型别名
使用 type 关键字创建类型别名:
type Kilometers = i32;
fn main() {
let distance: Kilometers = 100;
let another: i32 = 50;
// Kilometers 和 i32 完全相同
let total = distance + another; // ✅ 可以相加
}
类型推导
Rust 的类型推导非常强大:
fn main() {
// 从使用方式推导
let mut v = Vec::new(); // 类型未知
v.push(1); // 现在编译器知道 v 是 Vec<i32>
// 从返回类型推导
let x = "42".parse().expect("Not a number!"); // ❌ 编译错误:类型不明确
let x: i32 = "42".parse().expect("Not a number!"); // ✅ 明确类型
}
内存大小
查看类型的内存大小:
use std::mem;
fn main() {
println!("i32: {} bytes", mem::size_of::<i32>()); // 4
println!("f64: {} bytes", mem::size_of::<f64>()); // 8
println!("bool: {} bytes", mem::size_of::<bool>()); // 1
println!("char: {} bytes", mem::size_of::<char>()); // 4
println!("(i32, f64, u8): {} bytes",
mem::size_of::<(i32, f64, u8)>()); // 16(有对齐填充)
println!("[i32; 10]: {} bytes",
mem::size_of::<[i32; 10]>()); // 40
}
实践建议
✅ 推荐做法
fn main() {
// 1. 使用类型推导(除非有歧义)
let count = 42; // 清晰,推导为 i32
// 2. 在需要时标注类型
let parsed: i64 = "100".parse().unwrap();
// 3. 使用 usize/isize 作为索引和大小
let arr = [1, 2, 3];
for i in 0..arr.len() { // len() 返回 usize
println!("{}", arr[i]);
}
// 4. 优先使用 f64 而不是 f32
let pi = 3.14159; // 默认 f64,精度更高
}
❌ 避免做法
fn main() {
// ❌ 过度标注类型(类型推导已足够)
let x: i32 = 5;
let y: i32 = 10;
let sum: i32 = x + y;
// ❌ 不必要的类型转换
let a: i32 = 10;
let b: i32 = a as i32; // 多余
// ❌ 混用不同整数类型
let x: i32 = 10;
let y: i64 = 20;
// let z = x + y; // ❌ 需要显式转换
}
练习题
练习 1:计算内存占用
fn main() {
let data = (42u8, 3.14f64, 'A', [1i32, 2, 3]);
// 问:data 占用多少字节?
}
查看答案
use std::mem;
fn main() {
let data = (42u8, 3.14f64, 'A', [1i32, 2, 3]);
println!("{} bytes", mem::size_of_val(&data)); // 32 bytes
// u8(1) + padding(7) + f64(8) + char(4) + [i32;3](12) = 32
}
练习 2:类型转换
修复以下代码:
fn main() {
let x: u8 = 255;
let y: u16 = x;
println!("{}", y);
}
查看答案
fn main() {
let x: u8 = 255;
let y: u16 = x as u16; // 显式转换
println!("{}", y); // 255
}
小结
- ✅ Rust 是静态类型语言,编译时确定类型
- ✅ 标量类型:整数、浮点数、布尔、字符
- ✅ 复合类型:元组、数组
- ✅ 整数默认
i32,浮点数默认f64 - ✅
char是 4 字节的 Unicode 标量值 - ✅ 数组长度固定,栈分配;Vec 长度动态,堆分配
- ✅ 不会隐式类型转换,使用
as显式转换 - ✅ 使用
std::mem::size_of查看类型大小
下一步,我们将深入学习 Rust 的所有权系统。