Rust专项——掌握Rust的类型系统
引言
数据类型是编程语言的基础,它决定了数据在内存中的存储方式和可执行的操作。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 平台相关类型:isize 和 usize
isize 和 usize 的大小取决于目标平台的指针大小:
- 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 f32 和 f64
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的布尔类型只有两个值:true 和 false,占用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
对于可能失败的类型转换,使用 TryFrom 和 TryInto:
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 |
与数组索引匹配 |
| 计数器 | u32 或 u64 |
无符号,范围大 |
| 浮点数 | f64 |
默认,精度更高 |
| 布尔值 | bool |
唯一选择 |
| Unicode字符 | char |
支持所有Unicode |
| 固定大小集合 | [T; N] |
栈分配,性能好 |
| 可变字符串 | String |
可增长 |
| 字符串参数 | &str |
灵活,可接受多种类型 |
14.2 类型转换建议
- ✅ 优先使用安全的转换方法(如
parse())而不是as - ✅ 处理转换可能失败的情况,使用
match或if let - ✅ 注意整数溢出,在关键位置使用
checked_*方法 - ✅ 避免不必要的类型转换,保持类型一致性
14.3 性能考虑
- 整数类型:
i32和u32在大多数平台上性能最佳 - 浮点数:根据精度需求选择
f32或f64 - 数组vs向量:固定大小用数组,可变大小用
Vec - 字符串:尽可能使用
&str,只有在需要修改时才使用String
15. 总结
核心要点回顾
- 标量类型:整数(多种大小)、浮点数、布尔值、字符
- 复合类型:元组(多种类型)、数组(固定大小同类型)
- 字符串类型:
&str(不可变切片)和String(可变字符串) - 类型转换:使用
as、parse()或相关方法 - 类型安全:Rust在编译时检查类型,防止运行时错误
关键特性
- ✅ 静态类型系统:编译时类型检查
- ✅ 类型推断:减少类型注解
- ✅ 零成本抽象:高级类型不带来运行时开销
- ✅ 内存安全:类型系统保证内存安全
下一步学习
掌握了数据类型后,下一步我们将学习:
- 所有权系统:理解Rust的核心概念
- 引用与借用:学习如何在不转移所有权的情况下使用数据
- 函数:深入了解函数的参数和返回值类型
新一代开源开发者平台 GitCode,通过集成代码托管服务、代码仓库以及可信赖的开源组件库,让开发者可以在云端进行代码托管和开发。旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。
更多推荐


所有评论(0)