【Rust】集合数据类型之Vector

本文详细介绍了Rust中的Vector数据结构,包括新建、修改、获取(不可变与可变)、遍历、存储不同类型的值及销毁等操作。强调了Rust的借用检查器和所有权规则在处理Vector时的重要性,以及如何通过枚举和trait对象来存储不同类型的值。文章还提及了Vector的容量与长度的区别,并指出在销毁时,Vector及其内容会被一并清理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Rust 标准库中定义类 集合collections)数据结构。不同于内建的数组和元组类型,这些集合指向的数据是储存在堆上的。因此,这些数据的数量不必在编译时就已知,并且还可以随着程序的运行增长或缩小。接下来详细介绍三个在 Rust 中广泛使用的集合:

  • vector:允许我们按顺序储存一系列数量可变的值
  • 字符串string:字符的集合。
  • 哈希 maphash map:允许我们将值(value)与一个特定的键(key)相关联。

vector

首先是 Vec<T>,也被称为 vector。它允许我们储存多个相同类型的值,所有值在内存中彼此相邻排列。类似于其他语言中的数组。

新建 Vector

调用 Vec::new 函数创建一个新的空 vector:

let v: Vec<i32> = Vec::new(); 

💡 这里我们增加了一个类型标注。因为没有向这个 vector 中插入任何值,Rust 并不知道我们想要储存什么类型的元素。

Vec 是一个由标准库提供的类型,它可以存放任何类型。Vec<i32> 是泛型,告诉编译器你创建存放什么类型的 Vec。而在大多数情况中,只要你在创建时初始化值,编译器就可以推断出想要存放的类型。

为了方便,Rust 提供了 vec! 宏,来帮助我们方便地初始值来创建一个 Vec

let v = vec![1, 2, 3]; 

修改 Vector

可使用 push() 方法在队尾增加一个值:

// 如果想要能够改变它的值,必须使用 mut 关键字使其可变
let mut v = vec![1, 2, 3];
v.push(4);
v.push(5); 

获取 Vector

Rust 提供了两种方法:

// 索引语法:
let v = vec![1, 2, 3, 4, 5];

let third: &i32 = &v[2];
println!("The third element is {}", third);

// get 方法
match v.get(2) {Some(third) => println!("The third element is {}", third),None => println!("There is no third element."),
} 

对于索引语法,当引用一个不存在的元素时 Rust 会造成 panic。Rust 认为尝试访问超过 vector 结尾的元素是一个严重错误,这时应该使程序崩溃。而对于 get 方法,当被传递了一个数组外的索引值时,它不会 panic 而是返回 None

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

let does_not_exist = &v[100];
let does_not_exist = v.get(100); 

💡 当我们面临索引可能来源于用户输入的数字这种不确定的情况时,我们希望在发生越界时不会直接 panic 而是正常做出对应操作,就可以使用 get 方法。

Immutable

一旦程序获取了一个有效的引用,借用检查器将会执行所有权和借用规则来确保 vector 内容的这个引用和任何其他引用保持有效。

观察下面的代码:

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

let first = &v[0];

v.push(6);

println!("The first element is: {}", first); 

我第一次看到这段代码感觉是完全没有问题的,在 C 语言中我可以这样写:

#include <iostream>
using namespace std;

int main() {int arr[10] = {1, 2, 3};// printf("%d%d", arr[1], arr[5]);int *p = arr;p[5] = 5;printf("%d%d", p[1], p[5]);return 0;
} 

但在 Rust 中,这样是无法通过编译的

你肯定想,为什么第一个元素的引用会关心 vector 结尾的变化?这是由于 vector 的工作方式:在 vector 的结尾增加新元素时,在没有足够空间将所有所有元素依次相邻存放的情况下,可能会要求分配新内存并将老的元素拷贝到新的空间中。这时,第一个元素的引用就指向了被释放的内存。借用规则阻止程序陷入这种状况。

💡 vector 的容量 capacity 是为将添加到 vector 上的 future 元素分配的空间量。请勿将其与 vector 的长度混淆,后者指定 vector 中的实际元素数量。 如果 vector 的长度超过其容量,则其容量将自动增加,但必须重新分配其元素。

例如,容量为 10 且长度为 0 的 vector 将是一个空的 vector,具有 10 个以上元素的空间。将 10 个或更少的元素压入 vector 不会改变其容量或引起重新分配。 但是,如果 vector 的长度增加到 11,则必须重新分配,这可能会很慢。因此如果你有需要,建议尽可能使用 Vec::with_capacity 来指定 vector 希望达到的大小。

遍历 Vector

我们可以遍历可变 vector 的每一个元素的可变引用来改变他们。

let mut v = vec![100, 32, 57];
for i in &mut v {*i += 50;
} 

💡 为了修改可变引用所指向的值,必须使用解引用运算符(*)获取 i 中的值。

存储不同类型的值

Rust 规定 vector 只能储存相同类型的值。这很不方便。很多情况下你需要储存一系列不同类型的值的用例。而在 Rust 中,枚举的成员都被定义为相同的枚举类型,所以当需要在 vector 中储存不同类型值时,我们可以定义并使用枚举:

enum SpreadsheetCell {Int(i32),Float(f64),Text(String),
}

let row = vec![SpreadsheetCell::Int(3),SpreadsheetCell::Text(String::from("blue")),SpreadsheetCell::Float(10.12),
]; 

Rust 在编译时就必须准确的知道 vector 中类型的原因:

  • 需要知道储存每个元素到底需要多少内存。
  • 可以准确的知道这个 vector 中允许什么类型。

💡 如果 Rust 允许 vector 存放任意类型,那么当对 vector 元素执行操作时一个或多个类型的值就有可能会造成错误。在 JavaScript 中,我们经常遇到的一个bug就是一个对象没有这个匹配的方法,静态类型能够避免这种问题,而使用枚举外加 match 意味着 Rust 能在编译时就保证总是会处理所有可能的情况。

如果在编写程序时不能肯定运行时会储存进 vector 的所有类型,枚举技术就行不通了。这时你需要使用 trait 对象,我们将在后面介绍到。

销毁 Vector

按照之前提到的作用域 scope 概念,vector 在其离开作用域时会被释放。

{let v = vec![1, 2, 3, 4];// ...

} // <- 这里 v 离开作用域并被丢弃 

当 vector 被丢弃时,所有其内容也会被丢弃,这意味着它所包含的整数也将被全部清理。

总结

今天我们介绍了 Rust 中的 Vector,在其他语言中我们通常把他叫做数组。Rust 的数组 Vec 是一个 (指针,容量,长度) 三元组,指向堆上一处连续的内存:

 ptrlencapacity +--------+--------+--------+ | 0x0123 |2 |4 | +--------+--------+--------+|v
Heap +--------+--------+--------+--------+ |'a' |'b' | uninit | uninit | +--------+--------+--------+--------+ 

接下来我们会介绍完剩下两个类型:StringHashMap。值得一提的是,Rust 中的 String 看起来可能并不是那么顺眼,这是因为字符串本身就是复杂的:如utf8编码是不定长的、遍历的效率太低无法满足下标访问的要求等等。

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值