C8 常用的集合
与数组和tuple不同,下面的集合都存放在heap上
- 存放在heap上
- 无需在编译时确认集合的大小
- 在运行时动态的变大或者变小
Vector
特性
- 形式: Vec
<T> - 由标准库提供
- 可以存储多个值
- 只存储相同类型的数据
- 值在内存中连续存放
创建Vector
创建一个空的vector
-
Vec::new函数
rustfn main(){ // 空的vector编译器无法推断类型,所以需要显式指定具体类型Vec<i32> // 但是下面有一些奇怪的特例 let v: Vec<i32> = Vec::new(); }
指定初始值的方式创建Vector
-
通常使用指定初始值的方式创建Vector:使用
vec!宏rustfn main(){ let v = vec![1,2,3]; }
更新Vector
添加元素
- 向Vector添加元素,使用
push方法
rust
fn main(){
let mut v = Vec::new();
v.push(2);
}
删除Vector
- 与其他struct一样,离开作用域就会被清理,里面元素也被清理
读取Vector的元素
- 有两种方式可以引用Vector里的值
-
索引:
&v[index] -
get 方法:
v.get(index)
注意:get方法的返回值类型是Option<T> -
两者的区别
索引 get 方法 调用方式 &v[index]v.get(index)返回值类型 和本身vec内部的值一致 Option <T>访问超出范围的索引(访问越界) 引发panicked 返回None
-
rust
fn main(){
let mut v = vec![0,1,2,3];
let aa = &v[2];
println!("aa: {}", aa);
match v.get(10) {
// 能取到这个值
Some(i) => {
println!("The value is {}", i)
},
// 取不到这个值,比如说index大于3
None => println!("None"),
}
}
所有权和借用规则
- 不能在统一作用域内同时用于可变和不可变引用
- 具体看下面的例子

遍历Vector中的值
- for 循环
rust
fn main(){
let v = vec![0,1,2,3];
for i in &v {
println!("{}", i);
}
}
对于可变的vector
rust
fn main(){
let mut v = vec![0,1,2,3];
for i in &mut v {
// TODO *i解引用
println!("{}", *i+50);
}
}
使用enum来存储多种数据类型并且存在Vector中
可附加数据的enum变体
rust
// 创建一个ExcelCell枚举类型,里面可以存储i32的Int。。。
enum ExcelCell{
Int(i32),
Text(String),
Float(f64),
}
fn main()
{
let vv = vec![
ExcelCell::Int(12),
ExcelCell::Float(6.33),
ExcelCell::Text("xiaoming".to_string())
];
}
String
String的难点
- Rust倾向于暴露可能的错误
- 字符串数据结构复杂
- rust使用UTF-8编码(TODO)
字符串是什么
- 字符串是Byte的集合
- 提供了一些方法可以将byte解析为文本
通常说的字符串是指String和&str,标准库中也有其他的字符串类型:OsString/OsStr/CString/CStr
- 在rust的核心语言层面,只有一个字符串类型:
字符串切片str(或&str)- 字符串切片:对存储在其他地方/UTF-8编码的字符串的引用
- 字符串字面值:存储在二进制文件中->也是字符串切片
- String类型
- 来自
标准库 - 可增长/修改/可获得所有权
- UTF-8编码
- 来自
创建String
Vector的很多操作都可以用于String
- 创建空的string:String::new()函数
- 创建包含初始值的String
to_string()方法,可用于实现了Display trait,包括字符串字面值

String::from()函数,从字面值创建String
更新String
push_str()方法,把一个字符串切片附加到String
rust
fn main()
{
let aa = "Hello";
let mut y = aa.to_string();
// push_str内部参数类型是&str
y.push_str(", world");
println!("{}",y);
}
push()方法,把单个字符附加到String
rust
fn main()
{
let aa = "Hell";
let mut y = aa.to_string();
y.push('o');
println!("{}",y);
}
+,拼接字符串- 要求
+前面是String类型,后面是&str
- 要求
rust
fn main()
{
let str1 = String::from("Hello");
let str2 = String::from(" World");
let st = str1 + &str2;
println!("{}",st);
}
在执行完let st = str1 + &str2;后str1的所有权失效,但是str2的所有权仍然保留

format!,连接多个字符串(类似println!)format!的优点:更简单,且不会获得参数的所有权
rust
let str1 = String::from("He");
let str2 = String::from("ll");
let str3 = String::from("o");
let s = str1 + "-" + &str2 + "-" + &str3;
等效于
rust
// let s = str1 + "-" + &str2 + "-" + &str3;
let s = format!("{}-{}-{}",str1,str2,str3);
String的内部表示
- 不支持索引访问
- 内部表示
- String是对Vec
<u8>的包装 - len() 方法
// str1.len()的返回值是2。但是对于有一些字符,它一个字符所占的字节数就不是1,所以s.len()就不一定是字符的个数。
// 所以导致了rust避免使用索引去访问String
- String是对Vec
rust
let str1 = String::from("He");
println!("{}",str1.len());
- Rust中三种看待字符串的方式:
- 字节:s.bytes()
- 标量值: s.chars()
- 字形簇(最接近所谓的'字母')
切割String
- 可以使用
[]和一个范围来创建字符串的切片- &str1[0...2] 需要谨慎使用,如果切割跨越了字符的边界就会panic
rust
let str1 = String::from("He1110");
let s = &str1[0..2]
遍历String
- 字节:s.bytes()
- 标量值: s.chars()
HashMap
2026/1/8