Rust 数据类型

每种语言都有数据类型,Rust 的类型系统有几个独特的设计:整数类型按位宽精确命名(不再有 C 那种含糊的 int/long);char 是 4 字节 Unicode(告别 ASCII 局限);数组长度是类型的一部分(更严格的边界检查)。这一篇我们逐一过一遍。

1. 四种标量类型

标量(scalar)就是"单个值"的类型。Rust 有四种:整数、浮点、布尔、字符。

fn main() {
    // ===== 整数:有符号 i + 无符号 u =====
    // i8 i16 i32 i64 i128 isize    有符号(可正可负)
    // u8 u16 u32 u64 u128 usize    无符号(非负)
    let a: i32 = -42;             // 32 位有符号
    let b: u64 = 100;             // 64 位无符号
    let c: usize = 0;             // 与平台相关(64 位机上是 64 位)
    // isize / usize 通常用作"索引"或"内存大小"

    // ===== 浮点 =====
    let pi: f64 = 3.14159;        // 64 位(默认)
    let half: f32 = 0.5;          // 32 位

    // ===== 布尔 =====
    let ok: bool = true;
    let no = false;

    // ===== 字符 =====
    // 注意:Rust 的 char 是 4 字节的 Unicode 单个标量值
    // 不是 C/Java 那种 1 字节的 ASCII!
    let letter: char = 'A';
    let chinese: char = '中';
    let emoji: char = '😀';
}

几个要点:

2. 整数类型详解

// 整数类型一览(位宽决定范围)
// i8:   -128 ~ 127
// u8:   0 ~ 255
// i16:  -32768 ~ 32767
// u16:  0 ~ 65535
// i32:  -21亿 ~ 21亿      最常用,默认推断类型
// u32:  0 ~ 42亿
// i64:  非常大,常见于时间戳/大数
// i128: 极大,慎用(性能略低)

// 整数字面量写法
let dec = 1_000;          // 十进制,下划线分隔易读
let hex = 0xFF;           // 十六进制,0x 前缀
let oct = 0o777;          // 八进制,0o 前缀
let bin = 0b1010;         // 二进制,0b 前缀
let byte = b'A';          // 字节(u8),b 前缀 + 单字符

// 整数溢出:debug 模式 panic,release 模式环绕
// 想要显式控制:用 wrapping_/checked_/saturating_ 方法
let (x, y) = (250i8, 100i8);
let sum = x.checked_add(y);   // None 表示溢出,安全

几个新手容易踩的坑:

3. 浮点类型

Rust 只有两种浮点:f32(单精度,4 字节)和 f64(双精度,8 字节)。不写类型时默认 f64,因为现代 CPU 上 f64 速度和 f32 几乎一样,且精度更高。浮点遵循 IEEE 754 标准,所有语言共同的特性(精度问题、NaN、无穷)Rust 都有。

注意:浮点比较相等要小心(0.1 + 0.2 != 0.3 这种经典问题)。需要精确小数(如金融)请用第三方 crate bigdecimalrust_decimal

4. 布尔与字符

5. 元组(tuple)

元组是固定长度、可以装不同类型的复合类型。当你需要把几个值"打包"传递,又不想专门定义结构体时,元组很方便:

fn main() {
    // 元组:固定长度,可以装不同类型
    let person: (&str, i32, bool) = ("小明", 20, true);

    // 用 .0 .1 .2 访问元素(从 0 开始)
    println!("姓名:{}", person.0);
    println!("年龄:{}", person.1);

    // 解构:把元组拆开赋给多个变量
    let (name, age, active) = person;
    println!("{} 今年 {} 岁", name, age);

    // 空元组叫 unit,写作 (),是"没有值"的类型
    let nothing: () = ();

    // 函数不写返回类型,实际返回的就是 ()
    fn greet() { println!("hi"); }
    let r = greet();
    // r 的类型是 ()
}

几个细节:

6. 数组(array)

数组是固定长度、同类型的集合,分配在上(不是堆)。和 Python list、JS array 不同,Rust 的数组一旦创建,长度永远不变——想变长请用 Vec(后面讲字符串和标准库时会涉及):

fn main() {
    // 数组:固定长度,所有元素同类型
    // 在栈上分配(不像 Vec 在堆上)
    let nums = [1, 2, 3, 4, 5];
    println!("第一个:{}", nums[0]);     // 1
    println!("长度:{}", nums.len());    // 5

    // 显式指定类型和长度:[类型; 长度]
    let bytes: [u8; 4] = [0, 1, 2, 3];

    // 用同一个值初始化:[值; 长度]
    let zeros = [0; 10];                // 10 个 0
    println!("{}", zeros[0]);           // 0

    // 越界访问会 panic(debug 会检查,release 也检查)
    // 数组访问是"安全"的,不像 C 会读垃圾内存
    // let x = nums[10];  // 运行时 panic:索引越界

    // 数组长度是类型的一部分!
    // [i32; 3] 和 [i32; 4] 是不同的类型,不能互相赋值
}

关键点:

7. 类型转换(as)

Rust 不会自动类型转换——i32 和 f64 不能直接相加,必须显式转换。这避免了 C/C++ 里大量隐式转换导致的 bug。转换用 as 关键字:

fn main() {
    let a = 5;            // 推断为 i32
    let b = 2.5;          // 推断为 f64

    // 不同类型之间不能直接运算,即使"看起来应该可以"
    // let c = a + b;     // 编译错误!i32 和 f64 不兼容

    // 必须显式转换,用 as 关键字
    let c = a as f64 + b;  // OK,a 转换成 f64
    println!("c = {}", c);  // 7.5

    // 整数与整数之间也要 as
    let big: i64 = 1_000_000;
    let small: i32 = big as i32;     // 可能截断!小心
    let from_float = 3.99 as i32;    // 3,直接截断小数部分

    // 字符与整数互转
    let ch = 'A';
    let code = ch as u32;            // 65
    let back = code as u8 as char;   // 'A'

as 转换是"可能丢失数据"的,编译器要求你显式承认这一点。比如 i64 转 i32 在值很大时会被截断。这是 Rust"显式胜于隐式"哲学的体现。

8. 字符串初识

字符串在 Rust 里比较特殊(有两种类型),专门有一篇深入讲。这里先记住:

9. println! 格式化

数据类型讲了这么多,顺便讲下怎么把它们打印出来。println! 的格式化能力很强:

fn main() {
    let name = "小明";
    let age = 20;
    let pi = 3.14159;

    // {} 默认输出
    println!("我叫 {},今年 {}", name, age);

    // {:?} 调试格式(适合数组、结构体)
    let arr = [1, 2, 3];
    println!("数组: {:?}", arr);     // [1, 2, 3]

    // :.N 浮点保留 N 位小数
    println!("pi = {:.2}", pi);      // 3.14

    // :>N 右对齐宽度 N,:<N 左对齐,:^N 居中
    println!("{:>10}", "hi");        // 8 空格 + hi
    println!("{:-^10}", "hi");       // ----hi----

    // 命名参数
    println!("{x} + {y} = {z}", x = 1, y = 2, z = 3);

    // 二进制/八进制/十六进制
    println!("bin: {:b}", 15);       // 1111
    println!("hex: {:x}", 255);      // ff
}

记住几个最常用的:{} 普通输出、{:?:} 调试输出、{:.N} 浮点 N 位小数。

小结

这一篇你认识了 Rust 的全部基础类型:整数(按位宽精确命名)、浮点、布尔、4 字节 Unicode 的 char、元组(混合类型)、数组(固定长度)。最重要的概念是"类型严格"——整数和浮点不能隐式转换,数组长度参与类型,这些约束换来的是编译期消灭大量 bug。下一篇看运算符,看看这些类型怎么参与运算。

← 上一篇 Rust 变量与可变性

下一篇 Rust 运算符

✈️💬