5分钟速读之Rust权威指南(十二)

678 阅读4分钟

通用集合类型-动态数组vector

所谓的动态数组,也就是Vec,允许我们在单个数据结构中存储多个相同类型的值,可以理解为TS中的array。

创建动态数组

使用Vec结构体创建一个成员是i32类型的数组:

let v: Vec<i32> = Vec::new();
println!("{:?}", v) // []

还可以使用vec!宏创建数组:

// 使用vec!可以快速创建数组,编译器将自动推断Vec的类型为Vec<i32>
let v2 = vec![1, 2, 3];
println!("{:?}", v2); // [1, 2, 3]

// 编译器会通过第一个元素类型来判断vec类型
// 如果后边包含不同的类型将报错
let v2 = vec![1, 2, "3"]; // 报错,预期第三个成员应该是i32类型

// 所以如果初始化时不存在第一个元素将报错因为编译器无法推断类型
let v3 = vec![] // 报错,无法推断成员类型

更新数组

为了在创建动态数组后将元素添加至其中,我们可以使用push方法:

// 由于要改变数组的内容,所以要添加mut关键字
let mut v4 = vec![1];

// 上面v4被推断成Vec<i32>,所以这里也只能添加i32类型
v4.push(2);

// 添加其他类型将报错
v4.push("3"); // 报错,预期一个i32类型

数组的销毁

当数组离开作用域被销毁时,成员也会被销毁:

{
  let v5 = vec![1, 2, 3];
  println!("{:?}", v5); // [1, 2, 3]
} // 作用域执行完后v5被销毁,所有成员也会被销毁

读取数组成员

读取动态数组成员有两种方式,下面是这个例子是直接根据索引访问,获取到成员的引用:

let v6 = vec![1, 2, 3, 4, 5];

// 访问索引为2的元素,获取到的成员的引用
let third: &i32 = &v6[2];
println!("第三个成员是:{}", third); // 3

// 如果访问不存在的成员
let no_exist: &i32 = &v6[100]; // 报错,索引超出了数组长度

还有一种使用get函数的访问方式,返回Option类型:

// get函数返回Option<&T>类型
let third = v6.get(2);

// 通过match获取其中的值
match third {
  Option::Some(num) => println!("第三个成员是:{}", num), // 3
  // 如果索引超出数组长度,不会报错,而是得到一个None
  Option::None => println!("不存在这个成员"),
}

获取数组成员数量:

v6.len() // 5

动态同样遵守所有权规则,可变引用不能和不可变引用同时存在:

let mut v6 = vec![1, 2, 3, 4, 5];

// 对数组的不可变引用
let first = &v6[0];

// 尝试更改第一个成员
v6[0] = 1; // 报错,更改成员使用了可变引用,但是first变量对数组有不可变引用

// 尝试添加一个成员
v6.push(6); // 报错,同样的问题

有人可能有疑问,上面的first获取了第一个成员的引用,后面push添加新的成员,感觉不会影响到first的引用啊?为什么会编译不通过呢?

事实上动态数组中的元素是连续存储的,插入新的元素后也许会没有足够多的空间将所有元素依次相邻地放下,这就需要分配新的内存空间,并将旧的元素移动到新的空间上。第一个元素的引用可能会因为插入行为而指向被释放的内存。

遍历动态数组中的值

使用for循环遍历不可变数组的引用:

let v7 = vec![1, 2, 3];
for i in &v7 {
  println!("{}", i);
  // 1
  // 2
  // 3
}
println!("{}", v7); // [1, 2, 3]

遍历过程中如果需要更改成员,需要使用可变引用:

let mut v8 = vec![1, 2, 3];
for i in &mut v8 {
  // 注意这里的星号表示“解引用”,用于获取成员的原始地址,后便会具体介绍
  *i += 50;
}
println!("{:?}", v8); // [51, 52, 53]

利用枚举在数组中存储不同类型的值

动态数组的成员都是同一类型的,当我们需要在动态数组中存储不同类型的元素时,可以定义并使用枚举来应对这种情况,因为枚举中的所有变体都被定义为了同一种枚举类型:

// 包含三种类型的枚举
#[derive(Debug)]
enum Item {
  Int(i32),
  Float(f64),
  Text(String),
}

// 方式一
let mut v9: Vec<Item> = Vec::new();
v9.push(Item::Int(1));
v9.push(Item::Float(1.1));
v9.push(Item::Text(String::from("abc")));

// 方式二
let v10 = vec![
  Item::Int(1),
  Item::Float(1.1),
  Item::Text(String::from("abc")),
];

println!("{:?}", v9); // [Int(1), Float(1.1), Text("abc")]
println!("{:?}", v10); // [Int(1), Float(1.1), Text("abc")]

这一章节只是简单介绍了Vec的使用,包括使用push在数组末尾添加成员,使用get获取指定索引的成员,其实数组还有很多操作方法,例如:pop可以删除末尾成员,详情可以去官方文档进一步学习。