数据类型

Rust 是静态类型(statically typed)语言,编译时必须知道所有变量的类型。

类型系统概览

Loading diagram...

标量类型

标量类型代表单个值。Rust 有四种主要的标量类型:

1. 整数类型

整数是没有小数部分的数字:

长度有符号无符号范围(有符号)范围(无符号)
8-biti8u8-128 ~ 1270 ~ 255
16-biti16u16-32,768 ~ 32,7670 ~ 65,535
32-biti32u32-2³¹ ~ 2³¹-10 ~ 2³²-1
64-biti64u64-2⁶³ ~ 2⁶³-10 ~ 2⁶⁴-1
128-biti128u128-2¹²⁷ ~ 2¹²⁷-10 ~ 2¹²⁸-1
archisizeusize取决于架构取决于架构
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. 布尔类型

布尔类型只有两个值:truefalse,占用 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
  • char4 字节的 Unicode 标量值
  • ✅ 数组长度固定,栈分配;Vec 长度动态,堆分配
  • 不会隐式类型转换,使用 as 显式转换
  • ✅ 使用 std::mem::size_of 查看类型大小

下一步,我们将深入学习 Rust 的所有权系统