原文标题: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"] }