Rust 数据类型:i32、String、Vec<T>
Rust 语言通过其严格但友好的类型系统,在保证内存安全的同时提供高性能。掌握核心数据类型是编写可靠 Rust 程序的第一步。本文聚焦三种最常用类型:i32(有符号整数)、String(可变字符串)和 Vec<T>(动态数组),手把手教你如何声明、初始化、修改和使用它们。
使用 i32 表示整数
i32 是 Rust 中默认的有符号 32 位整数类型,取值范围为 -2,147,483,648 到 2,147,483,647。它适用于大多数需要整数的场景,比如计数、索引或数学计算。
声明并初始化一个 i32 变量:
- 输入
let age: i32 = 25;—— 这里显式标注类型为i32。 - 或者省略类型,直接写
let score = 100i32;—— 通过字面量后缀i32推断类型。 - 也可以让编译器自动推断:
let count = 42;,只要后续使用中能确定是整数,Rust 会默认选i32。
对 i32 执行基本运算:
- 加法:
let total = 10i32 + 20; - 减法:
let diff = 50 - 30i32; - 乘法与除法:
let result = (100i32 * 2) / 5; - 注意:Rust 不会自动转换类型。若混合不同类型(如
i32和u32),必须显式转换,例如a as i32。
检查溢出行为(开发时):
- 在 debug 模式下,整数溢出会 panic(程序崩溃)。
- 在 release 模式下,默认采用“环绕”行为(如
i32::MAX + 1变成i32::MIN)。 - 若需显式处理溢出,使用
checked_add()等安全方法:let a = i32::MAX; let b = 1i32; match a.checked_add(b) { Some(sum) => println!("Sum: {}", sum), None => println!("Overflow!"), }
使用 String 存储文本
Rust 的 String 类型是堆分配的、可增长的、UTF-8 编码的字符串。它与不可变的字符串字面量 &str 不同,专用于需要修改或动态构建文本的场景。
创建一个 String:
- 从空字符串开始:
let mut s = String::new(); - 从字符串字面量转换:
let s = "hello".to_string(); - 或使用 from 方法:
let s = String::from("world");
向 String 添加内容:
- 追加另一个字符串:调用
push_str方法:let mut s = String::from("Hello"); s.push_str(", Rust!"); // s 现在是 "Hello, Rust!" - 追加单个字符:使用
push方法:let mut s = String::from("Hi"); s.push('!'); // s 现在是 "Hi!" - 拼接两个 String:使用
+运算符(注意所有权转移):let s1 = String::from("Hello"); let s2 = String::from("World"); let s3 = s1 + &s2; // s1 被 move,不能再用;s2 仅被借用
重要规则:
String是可变的,但必须显式声明为mut才能修改。- Rust 的字符串始终是合法的 UTF-8,任何操作都不会破坏编码。
- 避免频繁拼接大量字符串,性能较差;可改用
format!宏或String::with_capacity预分配空间。
使用 Vec<T> 管理动态数组
Vec<T>(读作“vector of T”)是 Rust 的标准动态数组类型,可在运行时增长或缩小,存储任意相同类型的元素。
创建一个 Vec<T>:
- 创建空向量(需指定类型):
let mut numbers: Vec<i32> = Vec::new(); - 使用宏快速初始化:
let colors = vec!["red", "green", "blue"]; // 类型自动推断为 Vec<&str> - 预分配容量以提升性能:
let mut v = Vec::with_capacity(10); // 预留空间,避免频繁重分配
向 Vec 添加和访问元素:
- 在末尾添加元素:调用
push:let mut nums = vec![1, 2, 3]; nums.push(4); // nums 现在是 [1, 2, 3, 4] - 通过索引读取元素:使用方括号:
let first = nums[0]; // 获取第一个元素(索引从 0 开始) - 安全访问(避免 panic):使用
get方法返回Option:match nums.get(10) { Some(value) => println!("Value: {}", value), None => println!("Index out of bounds"), }
修改和删除元素:
- 修改指定位置的值:
nums[0] = 100; // 将第一个元素改为 100 - 移除最后一个元素:调用
pop:let last = nums.pop(); // 返回 Option<i32> - 移除中间元素(代价较高,因需移动后续元素):
let removed = nums.remove(1); // 移除索引 1 处的元素
遍历 Vec:
- 只读遍历:
for num in &nums { println!("{}", num); } - 可变遍历(修改每个元素):
for num in &mut nums { *num += 1; // 解引用后加 1 }
类型之间的典型协作场景
在实际程序中,这三种类型经常一起使用。例如,解析用户输入的一串数字并存储:
use std::io;
fn main() {
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read line");
let numbers: Vec<i32> = input
.trim()
.split_whitespace()
.map(|s| s.parse().expect("Not a number"))
.collect();
println!("You entered: {:?}", numbers);
}
执行流程如下:
- 创建
String存储用户输入。 - 读取一行文本到该
String。 - 分割字符串为单词,并将每个单词 解析为
i32。 - 收集结果到
Vec<i32>中。
此例展示了 String 作为输入缓冲区、i32 作为数值载体、Vec<i32> 作为容器的协同工作方式。
常见错误与规避方法
下表总结新手在使用这三种类型时常犯的错误及解决方案:
| 错误现象 | 原因 | 正确做法 |
|---|---|---|
编译报错 “cannot find method push_str on type &str” |
试图在字符串字面量(&str)上调用 push_str |
先转换为 String:let mut s = "hello".to_string(); |
| “mismatched types” when adding numbers | 混用了 i32 和 u32 等不同整数类型 |
统一类型,或显式转换:a as i32 |
| 程序 panic:“index out of bounds” | 用 vec[index] 访问不存在的索引 |
改用 vec.get(index) 并处理 None |
| “cannot borrow as mutable” | 忘记将变量声明为 mut |
写 let mut v = Vec::new(); 而非 let v = ... |
记住:Rust 的编译器错误信息非常精准。遇到问题时,仔细阅读错误提示,通常能直接定位到类型不匹配或可变性缺失的问题。

暂无评论,快来抢沙发吧!