【译】Rust中的Vec类型

2,583 阅读5分钟

原文标题:Rust vectors
原文链接:saidvandeklundert.net/learn/2021-…
公众号: Rust 碎碎念
翻译 by: Praying

vector 是什么?

vector 可以被看作是一个可以动态调整大小的数组。它是一种可以存放一串元素的数据结构,且同一 vector 中的元素必须类型相同。如果需要的话,你也可以选择使用枚举(enum)类型,从而实现在 vector 内存储不同类型的元素(后面会有更为详细的介绍)。

在 Rust 中,vector 由三部分组成:

  • 指向堆上的 vector 数据的指针
  • 长度
  • 容量

这些参数被保存在一个结构体中。在处理内部的元素序列时,vector 为我们提供了很多便利,我们将在后面看到。

让我们从创建一个 vector 开始:

let mut vec: Vec<i32> = Vec::with_capacity(6);
vec.push(1);
vec.push(2);

这里我们用了with_capacity,尽管它不是必须的,但是 Rust 推荐我们尽可能地指定 vector 的期望容量。通过前面的定义,我们就有一个长度(length)为 0,容量(capacity)为 6 的空的 vector。接下来,我们往 vector 里面放入两个元素。现在,vector 里包含值[1,2],此时,长度变为 2,容量保持不变。我们的 vector 如下图所示:

为了更好的理解 vector,我们可以在屏幕上打印出它的几个属性:

println!("--------------------------------------------");
println!("Values inside vec: {:?}", vec);
println!("Length of the vec: {:?}", vec.len());
println!("Capacity of the vec: {:?}", vec.capacity());
println!("--------------------------------------------");

代码输出如下:

--------------------------------------------
Values inside vec: [1, 2]
Length of the vec: 2
Capacity of the vec: 6
--------------------------------------------

我们在代码里输出了 vector 的长度和容量信息。

现在,如果我们准备再往里面放入 5 个元素,那么元素数量超出了 vector 的容量,Rust 会重新调整 vector 的大小。调整的过程主要是创建一个容量为当前容量两倍的新的 vector,并把旧的 vector 里的元素拷贝过去,为了看到这个动作,我们可以执行下面的代码:

let mut vector = vec![3, 4, 5, 6, 7];
vec.append(&mut vector);
println!("--------------------------------------------");
println!("Length of the vec: {:?}", vec.len());
println!("Capacity of the vec: {:?}", vec.capacity());
println!("--------------------------------------------");

执行完上面的代码后,我们将会得到下面的信息:

--------------------------------------------
Length of the vec: 7
Capacity of the vec: 12
--------------------------------------------

vector 的常见操作

接下来是关于如何使用 vector 的一些示例。vector 在操作之后的内容信息大部分都通过注释详细给出,(大部分)注释内容是通过println!("{:?}", x)语句打印变量得到。简单起见,我移除了打印语句。

首先,创建一个空的 vector 并放入一些元素:

let mut vector: Vec<i32> = Vec::new(); // []
vector.push(0); // [0]
vector.push(1); // [0, 1]
vector.push(2); // [0, 1, 2]

移除 vector 的最后一个元素,并得到一个返回的 Option,其中包含最后一个元素的值:

vector.pop(); // Some(2)
vector; // [0, 1]

从 vector 中检索值:

vector[1] // 1

当你指向一个不存在的索引位置时,上面的代码会 panic。为了安全地访问一个索引位置,我们可以使用.get(或get_mut()获得一个可变引用):

vector.get(1) // Some(1)
vector.get(100) // None

下面我们使用宏创建第二个 vector:

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

我们使用remove从 vec 中移除某个索引位置的元素:

vec.remove(0);
vec; // [2, 3, 4, 5]

使用append方法,把一个 vector 中的所有元素移动到调用该方法的 vector 中:

vector.append(&mut vec);
vec; // []
vector; // [0, 1, 2, 3, 4, 5]

检查一个 vector 是否为空:

vec.is_empty(); // true
vector.is_empty(); // false

返回一个 vector 的长度:

vector.len(); // 6

迭代一个 vector。在定义迭代器(iterator)之后,我们对他循环:

let vecter_iterator = vector.iter();
for elem in vecter_iterator {
    println!("{}", elem);
}
/*
0
1
2
3
4
5
*/

前面的这些方法都不会修改 vector 内部的元素。要想迭代并修改其中的元素,我们可以使用下面的方法:

let vecter_iterator_m = vector.iter_mut();
for elem in vecter_iterator_m {
    *elem = *elem * 2;
}
println!("{:?}", vector); // [0, 2, 4, 6, 8, 10]

如果想要确认一个值是否在 vector 中:

vector.contains(&200); // false
vector.contains(&2); // true

在 vector 中插入一个元素:

vector.insert(2, 1);
vector; // [0, 2, 1, 4, 6, 8, 10]

对 vector 排序并执行一次二分查找:

vector.sort()
vector; // [0, 1, 2, 4, 6, 8, 10]
vector.binary_search(&4); // Ok(3)
vector.binary_search(&400); // Err(7)

调整一个 vector 的大小,并把空位置的元素填充为 0:

vector.resize(10, 0);
vector; // [0, 1, 2, 4, 6, 8, 10, 0, 0, 0]

可以使用相同的方法来缩小 vector:

vector.resize(2, 0);
vector; // [0, 1]

使用关联变量的枚举( Enum with Variants)在同一 vector 内存储不同的类型:

#[derive(Debug)]
enum ManyAsOne {
    String(String),
    I32(i32),
    F64(f64),
}
let vec = vec![
    ManyAsOne::I32(65444),
    ManyAsOne::String(String::from("Hello world.")),
    ManyAsOne::String(String::from("Foo bar.")),
    ManyAsOne::F64(3.14159265),
    ManyAsOne::I32(1984),
];
for elem in vec {
    println!("{:?}", elem);
    match elem {
        ManyAsOne::I32(value) => println!("value: {}", value),
        ManyAsOne::String(value) => println!("value: {}", value),
        ManyAsOne::F64(value) => println!("value: {}", value),
    }
}

运行上面的代码会输出下面的信息:

I32(65444)
value: 65444
String("Hello world.")
value: Hello world.
String("Foo bar.")
value: Foo bar.
F64(3.14159265)
value: 3.14159265
I32(1984)
value: 1984

把一个 vector 放入一个 hashmap 中:

use std::collections::HashMap;
let mut job_results: HashMap<String, Vec<i32>> = HashMap::new();
job_results.insert(String::from("1"), vec![3, 2, 2, 2, 2]);
job_results.insert(String::from("2"), vec![2, 3, 2, 2, 2]);
job_results.insert(String::from("3"), vec![2, 2, 3, 2, 2]);
job_results;  // {"2": [2, 3, 2, 2, 2], "3": [2, 2, 3, 2, 2], "1": [3, 2, 2, 2, 2]}

最后,我们还可以把 vector 放入到一个结构体中:

#[derive(Debug)]
struct Person {
    name: String,
    age: u8,
    interests: Vec<String>,
}

let mut marie = Person {
    name: String::from("Marie"),
    age: 31,
    interests: vec![
        String::from("Rust"),
        String::from("Python"),
        String::from("History"),
    ],
};
marie.interests.push(String::from("Astronomy"));
println!("{:?}", marie);

运行上面的代码将会输出下面的信息:

Person { name: "Marie", age: 31, interests: ["Rust", "Python", "History", "Astronomy"] }