引言

数据类型是编程语言的基础,它决定了数据在内存中的存储方式和可执行的操作。Rust拥有丰富而精确的类型系统,这不仅有助于编写高效的代码,更重要的是在编译时就能发现类型错误,确保内存安全。

本文将全面介绍Rust的数据类型,包括:

  • 标量类型(Scalar Types):整数、浮点数、布尔值、字符
  • 复合类型(Compound Types):元组、数组
  • 字符串类型:String&str
  • 类型转换与方法
  • 最佳实践和性能考虑

通过本文的学习,你将能够:

  • 理解Rust的所有基本数据类型
  • 掌握不同数据类型的适用场景
  • 学会进行安全的类型转换
  • 避免常见的类型相关错误

1. Rust类型系统概述

1.1 静态类型系统

Rust是一个静态强类型语言,这意味着:

  • 所有变量的类型在编译时确定
  • 类型检查在编译时进行,而非运行时
  • 类型错误会在编译阶段被发现和报告
fn main() {
    let x = 5;        // 编译器推断类型为 i32
    let y = 3.14;     // 编译器推断类型为 f64
    
    // let z = x + y;  // 编译错误!不能直接相加不同类型
    // error[E0277]: cannot add `f64` to `i32`
    
    let z = x as f64 + y;  // 需要显式类型转换
    println!("{}", z);
}

1.2 类型分类

Rust的类型可以分为两大类:

类型分类 说明 示例
标量类型 单一值类型 整数、浮点数、布尔值、字符
复合类型 组合多个值 元组、数组

让我们逐一深入了解。

2. 整数类型(Integer Types)

2.1 有符号整数(Signed Integers)

有符号整数可以表示正数和负数,以 i 开头:

fn main() {
    let x: i8 = -128;      // 8位有符号整数,范围:-128 到 127
    let y: i16 = -32768;   // 16位有符号整数,范围:-32768 到 32767
    let z: i32 = -2_147_483_648;  // 32位有符号整数(默认类型)
    let w: i64 = -9_223_372_036_854_775_808;  // 64位有符号整数
    let v: i128 = -170_141_183_460_469_231_731_687_303_715_884_105_728;  // 128位
    
    println!("i8 示例: {}", x);
    println!("i16 示例: {}", y);
    println!("i32 示例: {}", z);
    println!("i64 示例: {}", w);
}

在这里插入图片描述

2.2 无符号整数(Unsigned Integers)

无符号整数只能表示非负数,以 u 开头:

fn main() {
    let x: u8 = 255;        // 8位无符号整数,范围:0 到 255
    let y: u16 = 65535;     // 16位无符号整数,范围:0 到 65535
    let z: u32 = 4_294_967_295;  // 32位无符号整数,范围:0 到 2^32-1
    let w: u64 = 18_446_744_073_709_551_615;  // 64位无符号整数
    
    println!("u8 示例: {}", x);
    println!("u16 示例: {}", y);
    println!("u32 示例: {}", z);
    println!("u64 示例: {}", w);
}

在这里插入图片描述

2.3 平台相关类型:isizeusize

isizeusize 的大小取决于目标平台的指针大小:

  • 32位平台:32位(4字节)
  • 64位平台:64位(8字节)
fn main() {
    let x: isize = -100;   // 平台相关的有符号整数
    let y: usize = 100;     // 平台相关的无符号整数(通常用于索引和大小)
    
    println!("isize 在64位平台: {} ({} 字节)", x, std::mem::size_of::<isize>());
    println!("usize 在64位平台: {} ({} 字节)", y, std::mem::size_of::<usize>());
    
    // usize 通常用于数组索引和大小
    let numbers = [1, 2, 3, 4, 5];
    let length: usize = numbers.len();
    println!("数组长度: {}", length);
}

在这里插入图片描述

2.4 整数类型完整对比表

类型 大小 最小值 最大值 常用场景
i8 1字节 -128 127 小范围数值
u8 1字节 0 255 字节值、颜色值
i16 2字节 -32,768 32,767 中等范围数值
u16 2字节 0 65,535 端口号、小计数器
i32 4字节 -2,147,483,648 2,147,483,647 默认整数类型
u32 4字节 0 4,294,967,295 一般计数器、ID
i64 8字节 -2^63 2^63-1 大整数、时间戳
u64 8字节 0 2^64-1 大计数器、文件大小
i128 16字节 -2^127 2^127-1 大数计算
u128 16字节 0 2^128-1 大数计算、加密
isize 指针大小 平台相关 平台相关 指针相关计算
usize 指针大小 0 平台相关 数组索引、长度

2.5 整数字面量

Rust支持多种整数字面量格式:

fn main() {
    // 十进制
    let decimal = 98_222;
    println!("十进制: {}", decimal);
    
    // 十六进制(以 0x 开头)
    let hex = 0xff;
    println!("十六进制 0xff = {}", hex);  // 255
    
    // 八进制(以 0o 开头)
    let octal = 0o77;
    println!("八进制 0o77 = {}", octal);  // 63
    
    // 二进制(以 0b 开头)
    let binary = 0b1111_0000;
    println!("二进制 0b1111_0000 = {}", binary);  // 240
    
    // 字节(仅限 u8,以 b 开头)
    let byte = b'A';
    println!("字节 b'A' = {}", byte);  // 65
    
    // 类型后缀
    let x = 42u8;      // u8 类型
    let y = 42i64;     // i64 类型
    let z = 42_u128;   // u128 类型(下划线可选)
    
    println!("u8: {}, i64: {}, u128: {}", x, y, z);
}

在这里插入图片描述

2.6 整数溢出处理

在调试模式下,Rust会检查整数溢出并panic;在发布模式下,会进行二进制补码回绕:

fn main() {
    // 在调试模式下,这会panic
    // let x: u8 = 255;
    // let y = x + 1;  // panic! 溢出
    
    // 使用 checked_* 方法安全处理
    let x: u8 = 255;
    match x.checked_add(1) {
        Some(result) => println!("结果: {}", result),
        None => println!("溢出!"),
    }
    
    // 使用 wrapping_* 方法进行回绕
    let x: u8 = 255;
    let y = x.wrapping_add(1);
    println!("255 + 1 (回绕) = {}", y);  // 0
    
    // 使用 saturating_* 方法饱和运算
    let x: u8 = 255;
    let y = x.saturating_add(1);
    println!("255 + 1 (饱和) = {}", y);  // 255(不改变)
}

3. 浮点数类型(Floating-Point Types)

3.1 f32f64

Rust有两种浮点数类型:

fn main() {
    let x: f32 = 3.14;   // 32位浮点数(单精度)
    let y: f64 = 3.141592653589793;  // 64位浮点数(双精度,默认类型)
    
    println!("f32: {}", x);
    println!("f64: {}", y);
    
    // 默认情况下,浮点数推断为 f64
    let z = 3.14;  // 类型为 f64
    println!("默认浮点类型: {}", z);
}

3.2 浮点数精度与性能

类型 大小 精度 性能 使用场景
f32 4字节 约7位有效数字 更快 节省内存、图形计算
f64 8字节 约15位有效数字 较慢 默认选择、科学计算
fn main() {
    // f32 精度示例
    let x: f32 = 0.1;
    let y: f32 = 0.2;
    println!("f32: 0.1 + 0.2 = {:.10}", x + y);  // 可能有精度误差
    
    // f64 精度示例
    let a: f64 = 0.1;
    let b: f64 = 0.2;
    println!("f64: 0.1 + 0.2 = {:.10}", a + b);  // 精度更高
}

3.3 浮点数特殊值

fn main() {
    // 正无穷大
    let inf: f64 = f64::INFINITY;
    println!("正无穷: {}", inf);
    
    // 负无穷大
    let neg_inf: f64 = f64::NEG_INFINITY;
    println!("负无穷: {}", neg_inf);
    
    // NaN(Not a Number)
    let nan: f64 = f64::NAN;
    println!("NaN: {}", nan);
    
    // 检查 NaN
    if nan.is_nan() {
        println!("这是一个 NaN 值");
    }
    
    // 检查是否为有限值
    let normal = 3.14;
    println!("3.14 是有限的: {}", normal.is_finite());
    println!("inf 是有限的: {}", inf.is_finite());
}

3.4 浮点数运算

fn main() {
    let x: f64 = 10.0;
    let y: f64 = 3.0;
    
    println!("加法: {} + {} = {}", x, y, x + y);
    println!("减法: {} - {} = {}", x, y, x - y);
    println!("乘法: {} * {} = {}", x, y, x * y);
    println!("除法: {} / {} = {:.2}", x, y, x / y);
    println!("取余: {} % {} = {}", x, y, x % y);
    
    // 数学函数
    println!("平方根: sqrt({}) = {:.2}", x, x.sqrt());
    println!("指数: exp({}) = {:.2}", 1.0, 1.0f64.exp());
    println!("对数: ln({}) = {:.2}", x, x.ln());
    println!("正弦: sin({}) = {:.2}", 3.14, 3.14_f64.sin());
}

4. 布尔类型(Boolean Type)

4.1 基本使用

Rust的布尔类型只有两个值:truefalse,占用1字节:

fn main() {
    let t = true;
    let f: bool = false;  // 显式类型注解
    
    println!("true: {}, false: {}", t, f);
    println!("布尔值大小: {} 字节", std::mem::size_of::<bool>());
}

4.2 布尔值的使用场景

fn main() {
    // 条件判断
    let is_rust_awesome = true;
    if is_rust_awesome {
        println!("Rust 很棒!");
    }
    
    // 逻辑运算
    let a = true;
    let b = false;
    
    println!("a && b = {}", a && b);  // false
    println!("a || b = {}", a || b);  // true
    println!("!a = {}", !a);          // false
    
    // 隐式布尔转换
    let number = 42;
    if number != 0 {  // 数字不能直接用作布尔值
        println!("数字不为零");
    }
}

5. 字符类型(Character Type)

5.1 Unicode字符

Rust的 char 类型是32位Unicode标量值,可以表示任何Unicode字符:

fn main() {
    let c: char = 'z';
    let z = 'ℤ';           // Unicode数学符号
    let heart = '❤';       // Emoji
    let chinese = '中';     // 中文字符
    
    println!("ASCII字符: {}", c);
    println!("数学符号: {}", z);
    println!("Emoji: {}", heart);
    println!("中文: {}", chinese);
    
    println!("字符大小: {} 字节", std::mem::size_of::<char>());
}

5.2 字符与字符串的区别

fn main() {
    // char: 单个字符,单引号
    let character = 'R';  // char 类型
    
    // &str: 字符串,双引号
    let string = "Rust";  // &str 类型
    
    // String: 可变的字符串
    let mut string_type = String::from("Rust");  // String 类型
    
    println!("字符: {}, 大小: {} 字节", character, std::mem::size_of::<char>());
    println!("字符串: {}", string);
    println!("可变字符串: {}", string_type);
    
    // 字符可以转换为数字
    let ascii_value = character as u8;
    println!("'R' 的ASCII值: {}", ascii_value);  // 82
}

5.3 Unicode操作

fn main() {
    let ch = '🦀';  // Rust的吉祥物
    
    // 检查字符属性
    println!("是字母: {}", ch.is_alphabetic());
    println!("是数字: {}", ch.is_numeric());
    println!("是空白: {}", ch.is_whitespace());
    
    // 转换为字符串
    let ch_string: String = ch.to_string();
    println!("转换为字符串: {}", ch_string);
    
    // 从Unicode码点创建字符
    let crab_emoji = char::from_u32(0x1F980).unwrap();
    println!("从码点创建: {}", crab_emoji);  // 🦀
}

6. 元组类型(Tuple Type)

6.1 元组基础

元组将多个不同类型的值组合成一个复合类型:

fn main() {
    // 创建元组
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    
    // 访问元组元素(使用索引)
    println!("第一个元素: {}", tup.0);
    println!("第二个元素: {}", tup.1);
    println!("第三个元素: {}", tup.2);
    
    // 解构元组
    let (x, y, z) = tup;
    println!("解构后: x={}, y={}, z={}", x, y, z);
}

6.2 元组的用途

fn main() {
    // 函数返回多个值
    fn calculate(x: i32, y: i32) -> (i32, i32, i32, i32) {
        (x + y, x - y, x * y, x / y)
    }
    
    let (sum, diff, prod, quot) = calculate(10, 3);
    println!("和: {}, 差: {}, 积: {}, 商: {}", sum, diff, prod, quot);
    
    // 元组可以包含不同类型的值
    let person: (&str, u32, bool) = ("Alice", 25, true);
    let (name, age, is_active) = person;
    println!("姓名: {}, 年龄: {}, 活跃: {}", name, age, is_active);
    
    // 空元组(单元类型)
    let empty = ();  // 单元类型,通常用作函数无返回值时
    println!("空元组: {:?}", empty);
}

6.3 嵌套元组

fn main() {
    // 元组可以嵌套
    let nested: ((i32, i32), (f64, f64)) = ((1, 2), (3.14, 2.71));
    
    println!("嵌套元组: {:?}", nested);
    println!("内部元组第一个元素: {}", nested.0 .0);  // 1
    println!("内部元组第二个元素: {}", nested.1 .1);  // 2.71
}

7. 数组类型(Array Type)

7.1 数组基础

数组是固定大小的同类型元素集合:

fn main() {
    // 创建数组(类型和长度必须明确)
    let a: [i32; 5] = [1, 2, 3, 4, 5];
    
    // 访问数组元素
    println!("第一个元素: {}", a[0]);
    println!("第二个元素: {}", a[1]);
    
    // 遍历数组
    for element in a.iter() {
        print!("{} ", element);
    }
    println!();
    
    // 使用索引访问(注意越界检查)
    let index = 2;
    match a.get(index) {
        Some(value) => println!("索引 {} 的值: {}", index, value),
        None => println!("索引 {} 超出范围", index),
    }
}

7.2 数组初始化

fn main() {
    // 方式1:显式初始化每个元素
    let a1: [i32; 5] = [1, 2, 3, 4, 5];
    
    // 方式2:使用相同值初始化
    let a2 = [0; 5];  // [0, 0, 0, 0, 0]
    println!("相同值数组: {:?}", a2);
    
    // 方式3:使用数组字面量
    let a3 = [10, 20, 30, 40, 50];
    
    // 数组大小是固定的,编译时确定
    println!("数组长度: {}", a3.len());
    println!("数组大小: {} 字节", std::mem::size_of::<[i32; 5]>());
}

7.3 数组 vs 切片

fn main() {
    let arr: [i32; 5] = [1, 2, 3, 4, 5];
    
    // 数组:固定大小,在栈上分配
    println!("数组: {:?}", arr);
    
    // 切片:数组的视图
    let slice: &[i32] = &arr[1..4];  // [2, 3, 4]
    println!("切片: {:?}", slice);
    
    // 切片可以动态大小
    println!("切片长度: {}", slice.len());
}

7.4 数组的常见操作

fn main() {
    let mut arr = [10, 20, 30, 40, 50];
    
    // 修改数组元素
    arr[0] = 100;
    println!("修改后: {:?}", arr);
    
    // 数组迭代
    for (index, value) in arr.iter().enumerate() {
        println!("索引 {}: 值 {}", index, value);
    }
    
    // 数组映射
    let doubled: Vec<i32> = arr.iter().map(|x| x * 2).collect();
    println!("翻倍后: {:?}", doubled);
    
    // 数组求和
    let sum: i32 = arr.iter().sum();
    println!("数组和: {}", sum);
    
    // 数组最大值/最小值
    let max = arr.iter().max();
    let min = arr.iter().min();
    println!("最大值: {:?}, 最小值: {:?}", max, min);
}

8. 字符串类型:String 和 &str

8.1 字符串切片 &str

&str 是不可变的字符串切片,通常指向字符串字面量:

fn main() {
    // 字符串字面量是 &str 类型
    let s: &str = "Hello, Rust!";
    
    // 字符串字面量存储在程序的只读数据段
    let s1 = "Hello";
    let s2 = "Hello";
    // s1 和 s2 可能指向同一块内存
    
    println!("字符串切片: {}", s);
    println!("长度: {}", s.len());
    println!("字节数: {}", s.as_bytes().len());
}

8.2 字符串类型 String

String 是可变的、可增长的字符串,在堆上分配:

fn main() {
    // 创建 String 的几种方式
    let s1 = String::from("Hello");
    let s2 = "World".to_string();
    let s3 = String::new();
    
    println!("s1: {}", s1);
    println!("s2: {}", s2);
    println!("s3: '{}' (空字符串)", s3);
    
    // String 是可变的
    let mut s = String::from("Hello");
    s.push_str(", Rust!");
    s.push('!');  // 添加单个字符
    println!("可变字符串: {}", s);
}

8.3 String 和 &str 的区别

特性 &str String
可变性 ❌ 不可变 ✅ 可变(用 mut
内存位置 程序只读段或堆 堆上
大小 固定 可增长
所有权 借用 拥有
性能 更快(无堆分配) 较慢(堆分配)
使用场景 字符串字面量、函数参数 需要修改的字符串
fn main() {
    // &str: 字符串切片
    let s1: &str = "不可变的字符串";
    // s1.push_str("新内容");  // 错误!&str 不可变
    
    // String: 可变的字符串
    let mut s2 = String::from("可变的字符串");
    s2.push_str(",可以添加内容");
    println!("{}", s2);
    
    // 函数可以接受两种类型
    fn print_string(s: &str) {  // 接受 &str(也可接受 String 的引用)
        println!("{}", s);
    }
    
    print_string(s1);  // 直接传递 &str
    print_string(&s2);  // 传递 String 的引用(自动转换)
}

8.4 字符串操作

fn main() {
    let mut s = String::from("Hello, Rust!");
    
    // 字符串拼接
    s.push_str(" 添加的内容");
    println!("拼接后: {}", s);
    
    // 使用 + 运算符
    let s1 = String::from("Hello");
    let s2 = String::from(", World");
    let s3 = s1 + &s2;  // s1 被移动,不能继续使用
    println!("使用 +: {}", s3);
    
    // 使用 format! 宏
    let s4 = format!("{}-{}-{}", "Hello", "Rust", "World");
    println!("使用 format!: {}", s4);
    
    // 字符串切片
    let hello = &s3[0..5];
    println!("字符串切片: {}", hello);
    
    // 字符串遍历
    for c in s4.chars() {
        print!("{} ", c);
    }
    println!();
    
    // 字节遍历
    for b in s4.bytes() {
        print!("{} ", b);
    }
    println!();
}

9. 类型转换(Type Conversion)

9.1 显式类型转换(as)

使用 as 关键字进行显式类型转换:

fn main() {
    // 整数类型转换
    let x: i32 = 42;
    let y: i64 = x as i64;
    let z: u32 = x as u32;
    
    println!("i32 {} 转换为 i64: {}", x, y);
    println!("i32 {} 转换为 u32: {}", x, z);
    
    // 浮点数类型转换
    let f: f64 = 3.14;
    let f32_val = f as f32;
    let int_val = f as i32;  // 截断,不是四舍五入
    println!("f64 {} 转换为 f32: {}", f, f32_val);
    println!("f64 {} 转换为 i32: {}", f, int_val);
    
    // 字符转换
    let c = 'A';
    let ascii = c as u8;
    println!("字符 '{}' 的ASCII值: {}", c, ascii);
    
    // 指针转换(高级用法)
    let ptr = &x as *const i32 as usize;
    println!("指针值: {}", ptr);
}

9.2 安全的类型转换方法

fn main() {
    // 使用类型的方法进行转换
    let x: i32 = 42;
    
    // 整数转字符串
    let s1 = x.to_string();
    println!("整数转字符串: {}", s1);
    
    // 字符串转整数(返回 Result)
    let s2 = "42";
    match s2.parse::<i32>() {
        Ok(num) => println!("字符串转整数: {}", num),
        Err(e) => println!("转换失败: {}", e),
    }
    
    // 使用 expect 简化(如果确定会成功)
    let num: i32 = s2.parse().expect("转换失败");
    println!("转换结果: {}", num);
    
    // 浮点数转字符串
    let f: f64 = 3.14159;
    let f_str = f.to_string();
    println!("浮点数转字符串: {}", f_str);
    
    // 字符串转浮点数
    let f2: f64 = "3.14".parse().unwrap();
    println!("字符串转浮点数: {}", f2);
}

9.3 TryFrom 和 TryInto trait

对于可能失败的类型转换,使用 TryFromTryInto

use std::convert::TryFrom;

fn main() {
    // 尝试转换
    match i32::try_from(1000u16) {
        Ok(val) => println!("转换成功: {}", val),
        Err(_) => println!("转换失败:值超出范围"),
    }
    
    match u8::try_from(300u16) {
        Ok(val) => println!("转换成功: {}", val),
        Err(_) => println!("转换失败:300 超出 u8 范围"),
    }
}

10. 类型大小查询

使用 std::mem::size_of 查询类型大小:

fn main() {
    println!("=== Rust 类型大小 ===");
    println!("i8:    {} 字节", std::mem::size_of::<i8>());
    println!("i16:   {} 字节", std::mem::size_of::<i16>());
    println!("i32:   {} 字节", std::mem::size_of::<i32>());
    println!("i64:   {} 字节", std::mem::size_of::<i64>());
    println!("f32:   {} 字节", std::mem::size_of::<f32>());
    println!("f64:   {} 字节", std::mem::size_of::<f64>());
    println!("bool:  {} 字节", std::mem::size_of::<bool>());
    println!("char:  {} 字节", std::mem::size_of::<char>());
    println!("&str:  {} 字节 (指针大小)", std::mem::size_of::<&str>());
    println!("String: {} 字节 (指针大小)", std::mem::size_of::<String>());
    println!("[i32; 5]: {} 字节", std::mem::size_of::<[i32; 5]>());
}

11. 实战示例

示例1:数据类型转换器

fn main() {
    // 输入数据
    let integer: i32 = 42;
    let float: f64 = 3.14159;
    let string_number = "100";
    
    println!("=== 类型转换示例 ===");
    
    // 整数转浮点数
    let int_to_float = integer as f64;
    println!("{} (i32) -> {} (f64)", integer, int_to_float);
    
    // 浮点数转整数
    let float_to_int = float as i32;
    println!("{} (f64) -> {} (i32)", float, float_to_int);
    
    // 字符串转整数
    match string_number.parse::<i32>() {
        Ok(num) => println!("\"{}\" (str) -> {} (i32)", string_number, num),
        Err(e) => println!("转换失败: {}", e),
    }
    
    // 整数转字符串
    let int_to_string = integer.to_string();
    println!("{} (i32) -> \"{}\" (String)", integer, int_to_string);
}

示例2:学生成绩管理系统

fn main() {
    // 使用元组存储学生信息
    type Student = (String, u8, f64);  // (姓名, 年龄, 成绩)
    
    let students: [Student; 3] = [
        ("Alice".to_string(), 20, 95.5),
        ("Bob".to_string(), 21, 88.0),
        ("Charlie".to_string(), 19, 92.5),
    ];
    
    println!("=== 学生成绩管理系统 ===");
    for (name, age, score) in students.iter() {
        println!("姓名: {}, 年龄: {}, 成绩: {:.1}", name, age, score);
    }
    
    // 计算平均成绩
    let total: f64 = students.iter().map(|(_, _, score)| score).sum();
    let average = total / students.len() as f64;
    println!("平均成绩: {:.2}", average);
}

示例3:温度转换程序

fn main() {
    // 摄氏度转华氏度
    fn celsius_to_fahrenheit(c: f64) -> f64 {
        c * 9.0 / 5.0 + 32.0
    }
    
    // 华氏度转摄氏度
    fn fahrenheit_to_celsius(f: f64) -> f64 {
        (f - 32.0) * 5.0 / 9.0
    }
    
    let celsius = 25.0;
    let fahrenheit = celsius_to_fahrenheit(celsius);
    println!("{}°C = {:.1}°F", celsius, fahrenheit);
    
    let f = 77.0;
    let c = fahrenheit_to_celsius(f);
    println!("{}°F = {:.1}°C", f, c);
    
    // 使用元组返回多个值
    fn convert_temperature(c: f64) -> (f64, f64) {
        let f = celsius_to_fahrenheit(c);
        (c, f)
    }
    
    let (c_temp, f_temp) = convert_temperature(30.0);
    println!("转换结果: {}°C = {}°F", c_temp, f_temp);
}

示例4:字符统计工具

use std::collections::HashMap;

fn main() {
    let text = "Hello, Rust! 🦀";
    
    println!("=== 字符统计 ===");
    println!("原文: {}", text);
    println!("字符数: {}", text.chars().count());
    println!("字节数: {}", text.len());
    println!("是否为空: {}", text.is_empty());
    
    // 统计字母、数字、标点
    let mut letters = 0;
    let mut digits = 0;
    let mut others = 0;
    
    for ch in text.chars() {
        if ch.is_alphabetic() {
            letters += 1;
        } else if ch.is_numeric() {
            digits += 1;
        } else {
            others += 1;
        }
    }
    
    println!("字母: {}, 数字: {}, 其他: {}", letters, digits, others);
    
    // 统计每个字符
    let mut char_count: HashMap<char, usize> = HashMap::new();
    for ch in text.chars() {
        *char_count.entry(ch).or_insert(0) += 1;
    }
    
    println!("\n字符频率:");
    for (ch, count) in char_count.iter() {
        println!("  '{}': {}", ch, count);
    }
}

12. 扩展练习

练习1:类型基础

编写一个程序,声明所有基本数据类型的变量,打印它们的值和大小。

练习2:类型转换器

创建一个程序,接收用户输入(字符串),尝试将其转换为不同的数值类型(整数、浮点数),并处理转换错误。

练习3:学生管理系统

使用元组和数组创建一个学生管理系统,存储学生的姓名(String)、年龄(u8)和成绩数组([f64; 3]),计算每个学生的平均成绩。

练习4:温度转换器

实现一个完整的温度转换器,支持摄氏度、华氏度和开尔文之间的转换,使用元组返回多个温度值。

练习5:字符串分析器

创建一个字符串分析工具,统计字符串中的字母、数字、空格、标点符号和Emoji的数量。

练习6:数组操作

实现数组的常见操作:查找最大值/最小值、反转数组、数组去重、数组排序。

13. 常见错误与解决方案

错误1:整数溢出

fn main() {
    // 问题代码
    // let x: u8 = 255;
    // let y = x + 1;  // 在调试模式下会panic
    
    // 解决方案:使用 checked_* 或 wrapping_* 方法
    let x: u8 = 255;
    match x.checked_add(1) {
        Some(val) => println!("结果: {}", val),
        None => println!("溢出!"),
    }
}

错误2:数组越界

fn main() {
    let arr = [1, 2, 3, 4, 5];
    
    // 问题代码
    // println!("{}", arr[10]);  // 运行时panic
    
    // 解决方案:使用 get 方法
    match arr.get(10) {
        Some(val) => println!("值: {}", val),
        None => println!("索引超出范围"),
    }
}

错误3:类型不匹配

fn main() {
    let x: i32 = 42;
    let y: f64 = 3.14;
    
    // 问题代码
    // let z = x + y;  // 编译错误
    
    // 解决方案:显式类型转换
    let z = x as f64 + y;
    println!("结果: {}", z);
}

错误4:字符串索引错误

fn main() {
    let s = "Hello, 世界!";
    
    // 问题代码(Rust字符串不支持直接索引)
    // let ch = s[0];  // 编译错误
    
    // 解决方案:使用字符迭代或字节索引
    if let Some(ch) = s.chars().nth(0) {
        println!("第一个字符: {}", ch);
    }
}

14. 最佳实践

14.1 类型选择建议

场景 推荐类型 原因
一般整数 i32 默认类型,性能好
数组索引 usize 与数组索引匹配
计数器 u32u64 无符号,范围大
浮点数 f64 默认,精度更高
布尔值 bool 唯一选择
Unicode字符 char 支持所有Unicode
固定大小集合 [T; N] 栈分配,性能好
可变字符串 String 可增长
字符串参数 &str 灵活,可接受多种类型

14.2 类型转换建议

  • 优先使用安全的转换方法(如 parse())而不是 as
  • 处理转换可能失败的情况,使用 matchif let
  • 注意整数溢出,在关键位置使用 checked_* 方法
  • 避免不必要的类型转换,保持类型一致性

14.3 性能考虑

  • 整数类型i32u32 在大多数平台上性能最佳
  • 浮点数:根据精度需求选择 f32f64
  • 数组vs向量:固定大小用数组,可变大小用 Vec
  • 字符串:尽可能使用 &str,只有在需要修改时才使用 String

15. 总结

核心要点回顾

  1. 标量类型:整数(多种大小)、浮点数、布尔值、字符
  2. 复合类型:元组(多种类型)、数组(固定大小同类型)
  3. 字符串类型&str(不可变切片)和 String(可变字符串)
  4. 类型转换:使用 asparse() 或相关方法
  5. 类型安全:Rust在编译时检查类型,防止运行时错误

关键特性

  • 静态类型系统:编译时类型检查
  • 类型推断:减少类型注解
  • 零成本抽象:高级类型不带来运行时开销
  • 内存安全:类型系统保证内存安全

下一步学习

掌握了数据类型后,下一步我们将学习:

  • 所有权系统:理解Rust的核心概念
  • 引用与借用:学习如何在不转移所有权的情况下使用数据
  • 函数:深入了解函数的参数和返回值类型
Logo

新一代开源开发者平台 GitCode,通过集成代码托管服务、代码仓库以及可信赖的开源组件库,让开发者可以在云端进行代码托管和开发。旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐