作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :[email protected]
本文地址:https://blog.csdn.net/qq_28550263/article/details/130876619
【介绍】:本文介绍 Rust 语言中的向量,包括向量与数组的区别,向量的相关API的用法等等。
在 Rust 中,数组是一种固定大小的数据结构,需要在编译时指定长度。使用数组是很方便的,本节我们先回顾以下 Rust 语言中数组的一些基本特点。
当需要在栈上分配一块连续的内存空间时,由于数组在内存中是连续存储的,这意味着元素的存储是相邻的,可以通过索引快速访问元素。这在需要快速、直接访问元素的场景中非常有用。
// 创建一个长度为5的整数数组
let array: [i32; 5] = [1, 2, 3, 4, 5];
// 访问数组元素
println!("第一个元素: {}", array[0]);
println!("第三个元素: {}", array[2]);
当需要确切知道数组的长度,并希望在编译时进行检查时,由于 Rust 数组能够在编译时进行长度检查,从而确保不会越界访问。这在需要确保数组长度不变的情况下使用,以防止意外的错误。
// 创建一个长度为3的字符串数组
let array: [String; 3] = [
String::from("Hello"),
String::from("World"),
String::from("Rust"),
];
// 访问数组元素
for element in &array {
println!("{}", element);
}
需要注意的是,Rust 中的数组长度必须是一个 编译时常量,这意味着 在创建数组时需要确切地知道其长度,并且无法在运行时动态改变数组的大小。这种静态长度的限制使得数组在某些情况下不够灵活,无法适应动态的数据集合。
虽然 Rust 数组在一些场景下非常有用,但它有时候使用起来却不那么方便。首先,由于 Rust 数组的长度在编译时确定,并且在运行时无法改变。这意味着 一旦数组被创建,它的大小就是固定的,无法根据实际需求进行动态调整。这限制了数组在需要动态增长或缩小的场景下的灵活性。例如:
// 创建一个长度为3的整数数组
let array: [i32; 3] = [1, 2, 3];
// 无法动态改变数组长度
// array.push(4); // 错误!数组无法调用 `push` 方法
也正是由于数组的长度固定,当需要在数组已满时添加更多元素时,需要手动重新分配内存空间,并将现有数据复制到新的数组中。这个过程需要手动管理,增加了编码复杂性,并且可能导致性能损失。例如:
// 创建一个长度为3的整数数组
let mut array: [i32; 3] = [1, 2, 3];
// 当数组已满时,需要手动重新分配内存
let new_array: [i32; 6] = [array[0], array[1], array[2], 4, 5, 6];
array = new_array;
// 现在数组可以容纳更多元素
array[3] = 7;
array[4] = 8;
array[5] = 9;
在这个例子中,当需我们要向数组中添加更多元素时,需要手动重新分配内存,并复制现有数据到新数组的过程。这不仅繁琐,还容易引入错误和性能问题。
为了克服上面提到的数组的各种缺陷, Rust 提供了 向量(Vector)类型——这是一种可以在 运行时 动态改变大小,并且 自动处理内存分配 和 元素的移动 的数据结构。向量让我们在处理动态大小的数据集合时更加灵活和方便。
向量的长度可以在运行时动态改变,不像数组一样需要在编译时指定固定长度。这使得向量更适用于需要动态增长或缩小的场景,可以根据实际需求自动调整大小。
// 创建一个空的向量
let mut vector: Vec<i32> = Vec::new();
// 向向量添加元素
vector.push(1);
vector.push(2);
vector.push(3);
向量自动处理内存分配和元素的移动。当向量的长度超过当前分配的内存空间时,它会自动重新分配更大的内存空间,并将现有的元素复制到新的内存空间中。这样可以避免手动管理内存和复制数据的繁琐工作,从而减少了出错的可能性。
// 创建一个空的向量
let mut vector: Vec<i32> = Vec::new();
// 向向量添加元素
vector.push(1);
vector.push(2);
vector.push(3);
vector.push(4); // 超过当前内存空间,向量会自动重新分配更大的空间
// 自动处理内存分配和元素的移动
// 无需手动重新分配内存和复制数据
Rust 向量可以容纳不同类型的元素,而不限于特定的类型。这使得向量更加灵活,可以存储和处理各种类型的数据。
// 创建一个包含不同类型元素的向量
let mut vector: Vec<dyn std::fmt::Debug> = Vec::new();
vector.push(1);
vector.push("hello");
vector.push(true);
Rust 向量提供了丰富的方法和功能,用于操作和管理向量的元素。例如,可以使用迭代器遍历向量、使用 len()
获取向量长度、使用 contains()
检查元素是否存在等。
let vector = vec![1, 2, 3, 4, 5];
// 使用迭代器遍历向量
for element in &vector {
println!("{}", element);
}
// 获取向量长度
let length = vector.len();
println!("向量长度: {}", length);
// 检查元素是否存在
let contains_two = vector.contains(&2);
println!("向量是否包含2? {}", contains_two);
在 Rust 语言中,Vec 是一个结构体类型,定义在标准库的 alloc::vec::Vec 模块中(Struct alloc::vec::Vec
),表示一种连续的可增长数组类型,写为 Vec ,是“vector”的缩写。:
pub struct Vec<T, A: Allocator = Global> { /* private fields */ }
Vec 的源码请参考:https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#396-399 以下是部分摘抄:
impl<T> Vec<T> {
#[inline]
#[rustc_const_stable(feature = "const_vec_new", since = "1.39.0")]
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
pub const fn new() -> Self {
Vec { buf: RawVec::NEW, len: 0 }
}
// ... 省略其它方法
}
impl<T, A: Allocator> Vec<T, A> {
#[inline]
#[unstable(feature = "allocator_api", issue = "32838")]
pub const fn new_in(alloc: A) -> Self {
Vec { buf: RawVec::new_in(alloc), len: 0 }
}
// ... 省略其它方法
}
impl<T: Clone, A: Allocator> Vec<T, A> {
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "vec_resize", since = "1.5.0")]
pub fn resize(&mut self, new_len: usize, value: T) {
let len = self.len();
if new_len > len {
self.extend_with(new_len - len, ExtendElement(value))
} else {
self.truncate(new_len);
}
}
// ... 省略其它方法
}
impl<T, A: Allocator, const N: usize> Vec<[T; N], A> {
#[unstable(feature = "slice_flatten", issue = "95629")]
pub fn into_flattened(self) -> Vec<T, A> {
let (ptr, len, cap, alloc) = self.into_raw_parts_with_alloc();
let (new_len, new_cap) = if T::IS_ZST {
(len.checked_mul(N).expect("vec len overflow"), usize::MAX)
} else {
// SAFETY:
// - `cap * N` cannot overflow because the allocation is already in
// the address space.
// - Each `[T; N]` has `N` valid elements, so there are `len * N`
// valid elements in the allocation.
unsafe { (len.unchecked_mul(N), cap.unchecked_mul(N)) }
};
unsafe { Vec::<T, A>::from_raw_parts_in(ptr.cast(), new_len, new_cap, alloc) }
}
}
impl<T, A: Allocator> Vec<T, A> {
#[cfg(not(no_global_oom_handling))]
fn extend_with<E: ExtendWith<T>>(&mut self, n: usize, mut value: E) {
self.reserve(n);
unsafe {
let mut ptr = self.as_mut_ptr().add(self.len());
// Use SetLenOnDrop to work around bug where compiler
// might not realize the store through `ptr` through self.set_len()
// don't alias.
let mut local_len = SetLenOnDrop::new(&mut self.len);
// Write all elements except the last one
for _ in 1..n {
ptr::write(ptr, value.next());
ptr = ptr.add(1);
// Increment the length in every step in case next() panics
local_len.increment_len(1);
}
if n > 0 {
// We can write the last element directly without cloning needlessly
ptr::write(ptr, value.last());
local_len.increment_len(1);
}
}
}
}
impl<T: PartialEq, A: Allocator> Vec<T, A> {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn dedup(&mut self) {
self.dedup_by(|a, b| a == b)
}
}
// ... ...
如果你觉得这个 vector 的类型不能满足你的需要,其实也完全可以参考其代码自己实现一个。
又有点就有缺点,就像一句不知出处名言说所,“当上帝关上一扇窗的同时,就为你打开了另外的一扇窗”。本节我们讨论的是 在Rust 语言中,相比于 数组,向量的一些劣势。
由于向量是动态大小的,它需要在堆上分配内存来存储元素,并且还需要一些额外的内存空间来管理和追踪向量的大小和容量。这导致向量相比于数组具有更高的内存开销。
由于向量需要在运行时动态分配内存并进行元素的移动,这会带来一定的性能开销。向量的操作(如添加、删除、调整大小等)可能需要重新分配内存和复制数据,这可能会影响程序的性能。
向量在进行动态大小调整时,可能会导致堆内存中出现碎片化。当向量的容量不再满足需求,需要重新分配更大的内存空间时,如果没有连续的大块可用内存,就需要进行内存拷贝和重新分配,从而导致内存碎片化的问题。
向量的元素始终存储在 堆 上,并且需要使用指针进行访问。这在某些情况下可能引入额外的间接访问开销,尤其是当元素数量较少且局部性较高时,相比于数组的栈分配,可能会有一些性能上的损失。
由于向量在运行时动态调整大小,可能出现内存分配失败的情况。当向量无法分配足够的内存来存储元素时,可能会导致运行时错误,需要适当处理这些错误。
在了解 Rust 向量的优缺点后,进一步需要的就是如何依据其优缺点进行选用。本节讨论的是,在不同的需求场景下,如何在使用 数组 还是 向量中进行选择。
在 Rust 编程语言中,选择使用数组还是向量取决于以下几个方面的考虑:
// 创建一个长度为10的整数数组,存储像素数据
let pixels: [u8; 10] = [255, 0, 128, 64, 200, 100, 0, 0, 255, 255];
// 创建一个长度为3的字符串数组,存储命令行参数
let args: [String; 3] = [
String::from("program"),
String::from("arg1"),
String::from("arg2"),
];
在这个例子中,,数组被用来存储特定大小的数据集合,如像素数据和命令行参数。这些数据集合在编译时就已经具有固定的大小,并且 不需要在运行时动态调整大小。因此,数组是合适的选择,因为它提供了固定大小的数据结构,并且在编译时就确定了内存分配。
// 创建一个空的整数向量,用于存储动态输入数据
let mut numbers: Vec<i32> = Vec::new();
// 向向量添加动态输入的数据
let input = 42;
numbers.push(input);
// 创建一个动态大小的字符串向量,用于存储不同类型的数据
let mut data: Vec<dyn std::fmt::Debug> = Vec::new();
data.push(42);
data.push("hello");
data.push(true);
在这个例子中,数据集合的大小需要在运行时动态改变的,如动态输入数据和不同类型的数据集合。显然数组是不具备这样的能力的,因而我们选择使用了向量。
总而言之,要是能够使用数组的场景下,我们一般选用数组,从而获取更高的性能。如果数组实在无法满足要求时,我们考虑选择使用向量。
注意:不需要死背,只需要有个印象,然后记住常用又简单的一些方法如pop、push等等,在有需要的时候再来此查阅即可。
以下是逐个解析 Rust 语言中 Vec
结构体的方法:
方法名 | 描述 |
---|---|
allocator |
返回用于分配 Vec 内部缓冲区的分配器(allocator)。该方法返回 Option<&A> ,其中 A 是用于分配内存的分配器类型。 |
append |
将一个 Vec 的所有元素追加到另一个 Vec 中。这个方法将原始 Vec 中的元素转移所有权到目标 Vec 中。 |
as_mut_ptr |
返回指向 Vec 缓冲区第一个元素的可变指针。 |
as_mut_slice |
返回一个可变的切片,它引用整个 Vec 的内容。 |
as_ptr |
返回指向 Vec 缓冲区第一个元素的不可变指针。 |
as_slice |
返回一个不可变的切片,它引用整个 Vec 的内容。 |
capacity |
返回 Vec 当前分配的内存容量(以元素为单位)。 |
clear |
清空 Vec 中的所有元素,但不释放分配的内存。 |
dedup |
去除 Vec 中相邻的重复元素,只保留其中一个。 |
dedup_by |
使用指定的比较闭包(closure)去除 Vec 中相邻的重复元素。 |
dedup_by_key |
使用指定的键提取函数去除 Vec 中相邻的重复元素。 |
drain |
返回一个迭代器,它从 Vec 中移除并产生指定范围的元素。 |
drain_filter |
返回一个迭代器,它从 Vec 中移除并产生满足指定条件的元素。 |
extend_from_slice |
将一个切片的所有元素追加到 Vec 中。 |
extend_from_within |
使用指定的闭包从 Vec 中的一部分元素创建并追加新的元素。 |
from_raw_parts |
从给定的指针和长度创建一个 Vec ,可以是未初始化的。 |
from_raw_parts_in |
在指定的分配器上从给定的指针和长度创建一个 Vec 。 |
insert |
在指定索引位置插入一个元素,并将后续元素依次后移。 |
into_boxed_slice |
将 Vec 转换为一个 Box<[T]> ,将所有权转移到 Box 中。 |
into_flattened |
将 Vec 的嵌套结构展平为一个单独的 Vec 。 |
into_raw_parts |
将 Vec 转换为元组 (ptr, len, cap) ,并保留所有权。 |
into_raw_parts_with_alloc |
将 Vec 转换为元组 (ptr, len, cap, alloc) ,并保留所有权。 |
is_empty |
检查 Vec 是否为空。 |
leak |
将 Vec 转换为裸指针,并避免析构函数被调用。 |
len |
返回 Vec 中当前存储的元素数量。 |
new |
创建一个空的 Vec 。 |
new_in |
使用指定的分配器创建一个空的 Vec 。 |
pop |
移除并返回 Vec 中的最后一个元素。 |
push |
在 Vec 的末尾追加一个元素。 |
push_within_capacity |
在 Vec 的末尾追加一个元素,如果有足够的容量则不会分配新的内存。 |
remove |
移除指定索引位置的元素,并将后续元素依次前移。 |
reserve |
增加 Vec 的容量以容纳指定的附加元素数量。 |
reserve_exact |
增加 Vec 的容量以容纳指定的附加元素数量,使容量完全匹配。 |
resize |
更改 Vec 的长度,可能会插入默认值或删除元素。 |
resize_with |
更改 Vec 的长度,使用指定的闭包生成新元素。 |
retain |
保留满足指定条件的 Vec 中的元素,移除不满足条件的元素。 |
retain_mut |
在指定条件下保留 Vec 中的可变元素,移除不满足条件的元素。 |
set_len |
设置 Vec 的长度,可以超过当前容量,但是会产生未初始化的元素。 |
shrink_to |
将 Vec 的容量收缩到指定的大小。 |
shrink_to_fit |
将 Vec 的容量收缩到当前存储的元素数量。 |
spare_capacity_mut |
返回一个可变引用,允许直接访问 Vec 的未使用的缓冲区。 |
splice |
将另一个可迭代对象的元素替换为 Vec 中的一部分元素。 |
split_at_spare_mut |
将 Vec 拆分为两个可变切片,其中一个引用未使用的缓冲区。 |
split_off |
将 Vec 拆分为两个独立的 Vec ,根据指定的索引位置。 |
swap_remove |
移除指定索引位置的元素,并用最后一个元素替换它。 |
truncate |
将 Vec 截断为指定的长度。 |
try_reserve |
尝试增加 Vec 的容量以容纳指定的附加元素数量。 |
try_reserve_exact |
尝试增加 Vec 的容量以容纳指定的附加元素数量,使容量完全匹配。 |
with_capacity |
使用指定的初始容量创建一个空的 Vec 。 |
with_capacity_in |
使用指定的分配器和初始容量创建一个空的 Vec 。 |
capacity
方法该方法用于返回当前分配的内存容量(以元素为单位)。例如:
let vec = vec![1, 2, 3, 4, 5];
let capacity = vec.capacity();
println!("Capacity: {}", capacity);
reserve
方法 该方法用于增加 Vec
的容量以容纳指定的附加元素数量。例如:
let mut vec = vec![1, 2, 3];
vec.reserve(3); // 增加容量以容纳3个额外元素
println!("Capacity after reserve: {}", vec.capacity());
reserve_exact
方法该方法用于增加 Vec
的容量以容纳指定的附加元素数量,使容量完全匹配。例如:
let mut vec = vec![1, 2, 3];
vec.reserve_exact(4); // 增加容量以容纳4个额外元素
println!("Capacity after reserve_exact: {}", vec.capacity());
shrink_to
方法该方法用于将 Vec
的容量收缩到指定的大小。例如:
let mut vec = vec![1, 2, 3, 4, 5];
vec.shrink_to(3); // 将容量收缩到3
println!("Capacity after shrink_to: {}", vec.capacity());
shrink_to_fit
方法该方法用于将 Vec
的容量收缩到当前存储的元素数量。例如:
let mut vec = vec![1, 2, 3, 4, 5];
vec.pop(); // 移除一个元素
vec.shrink_to_fit(); // 将容量收缩到当前存储的元素数量
println!("Capacity after shrink_to_fit: {}", vec.capacity());
spare_capacity_mut
方法该方法用于返回一个可变引用,允许直接访问 Vec
的未使用的缓冲区。例如:
let mut vec = Vec::with_capacity(10);
let spare_capacity = vec.spare_capacity_mut();
unsafe {
for i in 0..spare_capacity {
// 直接修改未使用的缓冲区
*spare_capacity.offset(i as isize) = i;
}
vec.set_len(vec.len() + spare_capacity);
}
println!("Modified Vec: {:?}", vec);
with_capacity
方法:该方法用于使用指定的初始容量创建一个空的 Vec
。例如:
let vec: Vec<u32> = Vec::with_capacity(5);
println!("Capacity: {}", vec.capacity());
with_capacity_in
方法该方法用于使用指定的分配器和初始容量创建一个空的 Vec
。例如:
use std::alloc::System;
let vec: Vec<u32, System> = Vec::with_capacity_in(5);
println!("Capacity: {}", vec.capacity());
这些方法提供了对 Vec
容量和内存管理的功能,可以根据需求调整容量、预留内存或收缩容量,以优化内存使用和性能。
as_mut_ptr
方法该方法返回一个可变指针,指向 Vec
的第一个元素。例如:
let mut vec = vec![1, 2, 3];
let ptr = vec.as_mut_ptr();
unsafe {
*ptr = 5; // 修改第一个元素的值
}
println!("Modified Vec: {:?}", vec);
as_mut_slice
方法该方法返回一个可变切片,包含整个 Vec
的元素。例如:
let mut vec = vec![1, 2, 3];
let slice = vec.as_mut_slice();
slice[0] = 5; // 修改第一个元素的值
println!("Modified Vec: {:?}", vec);
as_ptr
方法该方法返回一个不可变指针,指向 Vec
的第一个元素。例如:
let vec = vec![1, 2, 3];
let ptr = vec.as_ptr();
unsafe {
println!("First element: {}", *ptr);
}
as_slice
方法该方法用于返回一个不可变切片,包含整个 Vec
的元素。例如:
let vec = vec![1, 2, 3];
let slice = vec.as_slice();
println!("Slice: {:?}", slice);
get
方法该方法用于根据索引获取 Vec
中的元素的不可变引用。例如:
let vec = vec![1, 2, 3];
if let Some(element) = vec.get(1) {
println!("Second element: {}", element);
} else {
println!("Element not found");
}
get_mut
方法该方法用于根据索引获取 Vec
中的元素的可变引用。例如:
let mut vec = vec![1, 2, 3];
if let Some(element) = vec.get_mut(1) {
*element = 5; // 修改第二个元素的值
} else {
println!("Element not found");
}
println!("Modified Vec: {:?}", vec);
insert
方法该方法用于在指定索引位置插入一个元素,并将后续元素依次后移。例如:
let mut vec = vec![1, 2, 3];
vec.insert(1, 4); // 在索引1插入元素4
println!("Modified Vec: {:?}", vec);
pop
方法该方法用于移除并返回 Vec
中的最后一个元素。例如:
let mut vec = vec![1, 2, 3];
if let Some(element) = vec.pop() {
println!("Popped element: {}", element);
} else {
println!("Vec is empty");
}
push
方法该方法用于在 Vec
的末尾添加一个元素。例如:
let mut vec = vec![1, 2, 3];
vec.push(4); // 在末尾添加元素4
println!("Modified Vec: {:?}", vec);
push_within_capacity
方法该方法用于在 Vec
的末尾添加一个元素,仅当容量允许时才进行扩展。例如:
let mut vec = Vec::with_capacity(3);
vec.push_within_capacity(1); // 添加元素1,容量不变
vec.push_within_capacity(2); // 添加元素2,容量不变
vec.push_within_capacity(3); // 添加元素3,容量不变
vec.push_within_capacity(4); // 添加元素4,容量扩展
println!("Modified Vec: {:?}", vec);
remove
方法该方法用于移除指定索引处的元素,并将后续元素依次前移。例如:
let mut vec = vec![1, 2, 3];
let removed = vec.remove(1); // 移除索引1处的元素
println!("Removed element: {}", removed);
println!("Modified Vec: {:?}", vec);
swap
方法该方法用于交换 Vec
中两个索引位置的元素。例如:
let mut vec = vec![1, 2, 3];
vec.swap(0, 2); // 交换索引0和索引2处的元素
println!("Modified Vec: {:?}", vec);
swap_remove
方法该方法用于移除指定索引处的元素,并用最后一个元素替换,可以减少移动其他元素的成本。例如:
let mut vec = vec![1, 2, 3];
let removed = vec.swap_remove(1); // 交换移除索引1处的元素
println!("Removed element: {}", removed);
println!("Modified Vec: {:?}", vec);
这些方法提供了对 Vec
元素的访问和操作的功能,可以通过索引、指针或切片来访问、插入、移除和修改元素,以及交换元素位置。使用这些方法可以灵活地操作 Vec
中的元素。
drain
方法该方法创建一个迭代器,用于在迭代过程中逐个移除 Vec
的元素。例如:
let mut vec = vec![1, 2, 3, 4, 5];
let drained: Vec<_> = vec.drain(1..4).collect(); // 移除索引1到3的元素
println!("Drained elements: {:?}", drained);
println!("Remaining Vec: {:?}", vec);
drain_filter
方法该方法创建一个迭代器,用于在迭代过程中逐个移除满足特定条件的 Vec
元素。例如:
let mut vec = vec![1, 2, 3, 4, 5];
let removed: Vec<_> = vec.drain_filter(|&x| x % 2 == 0).collect(); // 移除偶数元素
println!("Removed elements: {:?}", removed);
println!("Remaining Vec: {:?}", vec);
iter
方法该方法返回一个不可变迭代器,用于按顺序访问 Vec
的元素。例如:
let vec = vec![1, 2, 3, 4, 5];
for element in vec.iter() {
println!("Element: {}", element);
}
iter_mut
方法该方法返回一个可变迭代器,用于按顺序访问 Vec
的元素的可变引用。例如:
let mut vec = vec![1, 2, 3, 4, 5];
for element in vec.iter_mut() {
*element *= 2; // 修改元素的值
}
println!("Modified Vec: {:?}", vec);
这些方法提供了对 Vec
元素进行迭代和遍历的功能。drain
方法通过创建一个迭代器来移除元素,drain_filter
方法通过创建一个迭代器来移除满足特定条件的元素。iter
方法返回一个不可变迭代器,用于按顺序访问元素,而 iter_mut
方法返回一个可变迭代器,用于按顺序访问元素的可变引用。通过这些方法,可以方便地对 Vec
的元素进行迭代和操作。
append
方法该方法将另一个 Vec
的所有元素追加到当前 Vec
的末尾。例如:
let mut vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
vec1.append(&mut vec2); // 将 vec2 的元素追加到 vec1 的末尾
println!("Modified Vec1: {:?}", vec1);
println!("Modified Vec2: {:?}", vec2);
clear
方法该方法移除 Vec
的所有元素,将其长度设置为 0。例如:
let mut vec = vec![1, 2, 3];
vec.clear(); // 移除所有元素
println!("Cleared Vec: {:?}", vec);
dedup
方法该方法移除 Vec
中连续出现的重复元素,只保留一个副本。例如:
let mut vec = vec![1, 2, 2, 3, 3, 3, 4, 5];
vec.dedup(); // 移除连续出现的重复元素
println!("Modified Vec: {:?}", vec);
dedup_by
方法该方法根据自定义的比较函数,移除 Vec
中连续出现的满足特定条件的重复元素,只保留一个副本。例如:
let mut vec = vec![1, 2, 3, 4, 5, 6, 7];
vec.dedup_by(|a, b| a % 2 == b % 2); // 移除连续出现的奇偶数重复元素
println!("Modified Vec: {:?}", vec);
dedup_by_key
方法该方法根据自定义的键提取函数,移除 Vec
中连续出现的相同键的元素,只保留一个副本。例如:
let mut vec = vec!["apple", "banana", "orange", "pear"];
vec.dedup_by_key(|s| s.chars().next().unwrap()); // 移除连续出现的首字母相同的元素
println!("Modified Vec: {:?}", vec);
extend
方法该方法将一个可迭代对象中的所有元素追加到当前 Vec
的末尾。例如:
let mut vec = vec![1, 2, 3];
let other_vec
= vec![4, 5, 6];
vec.extend(other_vec); // 将 other_vec 中的元素追加到 vec 的末尾
println!("Modified Vec: {:?}", vec);
extend_from_slice
方法该方法将一个切片中的所有元素追加到当前 Vec
的末尾。例如:
let mut vec = vec![1, 2, 3];
let other_slice = &[4, 5, 6];
vec.extend_from_slice(other_slice); // 将 other_slice 中的元素追加到 vec 的末尾
println!("Modified Vec: {:?}", vec);
extend_from_within
方法该方法根据提供的索引,将 Vec
中的元素复制到指定位置。例如:
let mut vec = vec![1, 2, 3, 4, 5];
vec.extend_from_within(1..4); // 复制索引1到3的元素到末尾
println!("Modified Vec: {:?}", vec);
replace
方法该方法将 Vec
中指定位置的元素替换为新的元素,并返回被替换的旧元素。例如:
let mut vec = vec![1, 2, 3, 4, 5];
let old_element = vec.replace(2, 6); // 将索引为2的元素替换为6
println!("Old element: {:?}", old_element);
println!("Modified Vec: {:?}", vec);
resize
方法该方法修改 Vec
的长度,将其扩展或收缩到指定的大小,并使用给定的值填充新元素。例如:
let mut vec = vec![1, 2, 3];
vec.resize(5, 0); // 扩展 Vec 的长度到5,并用0填充新元素
println!("Modified Vec: {:?}", vec);
resize_with
方法该方法修改 Vec
的长度,将其扩展或收缩到指定的大小,并使用提供的闭包生成新元素。例如:
let mut vec = vec![1, 2, 3];
vec.resize_with(5, Default::default); // 扩展 Vec 的长度到5,并使用默认值生成新元素
println!("Modified Vec: {:?}", vec);
retain
方法该方法根据指定的条件保留 Vec
中满足条件的元素,移除不满足条件的元素。例如:
let mut vec = vec![1, 2, 3, 4, 5];
vec.retain(|&x| x % 2 == 0); // 保留偶数元素
println!("Modified Vec: {:?}", vec);
retain_mut
方法该方法根据指定的条件保留 Vec
中满足条件的元素的可变引用,移除不满足条件的元素的可变引用。例如:
let mut vec = vec![1, 2, 3, 4, 5];
vec.retain_mut(|x| {
*x *= 2; // 修改元素的值
*x % 4 == 0 // 保留能被4整除的元素
});
println!("Modified Vec: {:?}", vec);
set_len
方法该方法修改 Vec
的长度,将其设置为指定的长度,但不改变底层的内存分配。例如:
let mut vec = vec![1, 2, 3, 4, 5];
vec.set_len(3); // 设置 Vec 的长度为3
println!("Modified Vec: {:?}", vec);
splice
方法该方法替换 Vec
中指定范围的元素,并返回被替换的元素作为迭代器。例如:
let mut vec = vec![1, 2, 3, 4, 5];
let spliced: Vec<_> = vec.splice(1..4, vec![6, 7, 8]).collect(); // 替换索引1到3的元素为新的元素
println!("Spliced elements: {:?}", spliced);
println!("Modified Vec: {:?}", vec);
allocator
方法该方法返回用于分配和释放 Vec
内存的分配器。例如:
use std::alloc::System;
use std::alloc::Layout;
use std::mem::MaybeUninit;
let allocator = System;
let mut vec: Vec<i32> = Vec::new_in(allocator);
let layout = Layout::array::<i32>(10).unwrap();
vec.resize_with(10, || unsafe { MaybeUninit::uninit().assume_init() });
let allocated_size = vec.allocator().usable_size(&layout);
println!("Allocated size: {:?}", allocated_size);
is_empty
方法该方法检查 Vec
是否为空,即是否包含任何元素。例如:
let vec: Vec<i32> = Vec::new();
println!("Is empty: {:?}", vec.is_empty()); // 输出: Is empty: true
let vec = vec![1, 2, 3];
println!("Is empty: {:?}", vec.is_empty()); // 输出: Is empty: false
len
方法该方法返回 Vec
中元素的数量。例如:
let vec: Vec<i32> = Vec::new();
println!("Length: {:?}", vec.len()); // 输出: Length: 0
let vec = vec![1, 2, 3];
println!("Length: {:?}", vec.len()); // 输出: Length: 3
这些方法允许你查询关于 Vec
的状态和属性。allocator
方法返回用于分配和释放 Vec
内存的分配器,is_empty
方法检查 Vec
是否为空,len
方法返回 Vec
中元素的数量。通过这些方法,你可以获得关于 Vec
的有关信息,并进行相应的处理。
from_raw_parts
方法该方法接受一个裸指针、元素数量和容量,返回一个 Vec
,并拥有指定的内存区域。例如:
use std::mem;
let ptr = Box::into_raw(Box::new([1, 2, 3])) as *mut i32;
let len = 3;
let capacity = 3;
let vec = unsafe { Vec::from_raw_parts(ptr, len, capacity) };
println!("Vec: {:?}", vec);
from_raw_parts_in
方法该方法接受一个裸指针、元素数量、容量和分配器,返回一个使用指定分配器的 Vec
,并拥有指定的内存区域。例如:
use std::alloc::System;
use std::mem;
let allocator = System;
let ptr = allocator.alloc(Layout::array::<i32>(3).unwrap()) as *mut i32;
let len = 3;
let capacity = 3;
let vec = unsafe { Vec::from_raw_parts_in(ptr, len, capacity, allocator) };
println!("Vec: {:?}", vec);
into_boxed_slice
方法该方法将 Vec
转换为一个拥有其所有元素的 Box<[T]>
。例如:
let vec = vec![1, 2, 3];
let boxed_slice: Box<[i32]> = vec.into_boxed_slice();
println!("Boxed slice: {:?}", boxed_slice);
into_flattened
方法该方法将 Vec
转换为一个 Vec
,将内部的嵌套 Vec
展平。例如:
let vec = vec![vec![1, 2], vec![3, 4, 5], vec![6]];
let flattened: Vec<i32> = vec.into_flattened();
println!("Flattened Vec: {:?}", flattened);
into_raw_parts
方法该方法将 Vec
分解为其原始数据、长度和容量,并返回它们的元组。例如:
let vec = vec![1, 2, 3];
let (ptr, len, capacity) = vec.into_raw_parts();
println!("Pointer: {:p}", ptr);
println!("Length: {:?}", len);
println!("Capacity: {:?}", capacity);
into_raw_parts_with_alloc
方法该方法将 Vec
分解为其原始数据、长度、容量和分配器,并返回它们的元组。例如:
use std::alloc::System;
let allocator = System;
let vec = vec![1, 2, 3];
let (ptr, len, capacity, alloc) = vec.into_raw_parts_with_alloc(allocator);
println!("
Pointer: {:p}", ptr);
println!("Length: {:?}", len);
println!("Capacity: {:?}", capacity);
println!("Allocator: {:?}", alloc);
leak
方法该方法将 Vec
转换为一个静态生命周期的引用,并且不会执行内存释放。例如:
let vec: Vec<i32> = vec![1, 2, 3];
let leaked: &'static mut [i32] = Vec::leak(vec);
println!("Leaked slice: {:?}", leaked);
truncate
方法该方法修改 Vec
的长度,截断到指定的长度,丢弃超过长度的元素。例如:
let mut vec = vec![1, 2, 3, 4, 5];
vec.truncate(3); // 截断 Vec 的长度为3
println!("Truncated Vec: {:?}", vec);
try_reserve
方法该方法尝试增加 Vec
的容量,以至少能够容纳指定的元素数量。例如:
let mut vec = vec![1, 2, 3];
let new_len = vec.len() + 5;
if vec.try_reserve(new_len).is_ok() {
vec.extend(4..=8);
println!("Modified Vec: {:?}", vec);
} else {
println!("Failed to reserve capacity");
}
try_reserve_exact
方法该方法尝试增加 Vec
的容量,使其能够容纳精确指定的元素数量。例如:
let mut vec = vec![1, 2, 3];
let new_len = vec.len() + 5;
if vec.try_reserve_exact(new_len).is_ok() {
vec.extend(4..=8);
println!("Modified Vec: {:?}", vec);
} else {
println!("Failed to reserve exact capacity");
}
alloc::vec
宏Rust 语言中提供了 alloc::vec
宏,用于创建一个Vec包含参数的 Dust 向量 。
macro_rules! vec {
() => { ... };
($elem:expr; $n:expr) => { ... };
($($x:expr),+ $(,)?) => { ... };
}
使用 alloc::vec
宏,你可以通过在方括号内提供初始值来初始化向量。宏会根据提供的初始值计算出向量所需的容量,并在堆上分配足够的内存来存储这些值。你可以如下使用 alloc::vec
宏 来创建和初始化 Rust 向量:
use alloc::vec;
let vec = vec![1, 2, 3, 4, 5];
println!("Vector: {:?}", vec);
本例中,我们使用 alloc::vec
宏创建了一个包含 1、2、3、4、5 这6个元素的向量。宏根据提供的初始值计算出向量的长度,并在堆上分配了适当大小的内存来存储这些值。最后,我们打印出了创建的向量。
alloc::vec
宏还支持在初始值中使用重复的元素。例如,你可以使用 [0; n]
的形式来创建一个包含 n 个重复元素的向量:
use alloc::vec;
let vec = vec![0; 5];
println!("Vector: {:?}", vec);
这里,我们使用 alloc::vec
宏创建了一个包含 5 个重复的元素 0 的向量。
alloc::vec
宏在编译时会自动计算向量的容量,并确保分配足够的内存来存储初始值。这使得在创建向量时不需要手动指定容量,而是由编译器根据提供的初始值进行计算。这样可以提供更高的代码灵活性和可读性,同时减少了手动计算容量的繁琐工作。
注意:
alloc::vec
宏位于alloc
模块中,而不是标准库的根模块中。因此在使用之前需要通过use
语句将其导入到作用域中,即:use alloc::vec;
- 更多细节可以参考:https://doc.rust-lang.org/src/alloc/macros.rs.html#42-57
我们可以使用 for
循环来遍历向量中的元素,例如:
fn main() {
let vec = vec![1, 2, 3, 4, 5];
for item in &vec {
println!("Item: {}", item);
}
}
在上面的例子中,我们使用 for
循环遍历向量 vec
中的每个元素,并打印出元素的值。
不过需要指出的是,在 Rust 语言中使用 for 循环进行遍历的方式是一种语法糖,它隐藏了不过迭代器的使用。因此读者也可以参考使用迭代器的遍历方式。
在 Rust 中,迭代器(Iterator)是一种序列的抽象,它提供了一种统一的方式来遍历和处理序列中的元素。迭代器是 Rust 标准库中的一个重要组件,广泛应用于向量、哈希表、文件、字符串等数据结构和类型。
迭代器实现了 Iterator
trait,这个 trait 定义了一组方法,用于操作和处理序列中的元素。通过使用迭代器,我们可以以一种统一的方式处理不同类型的序列,无论是数组、向量、哈希表还是文件等。这样的设计使得代码更具表达力和灵活性,同时也提供了更好的性能和安全性。
如果我们需要对向量中的元素进行可变的操作,可以使用 iter_mut 方法获取一个可变迭代器。例如:
fn main() {
let vec = vec![1, 2, 3, 4, 5];
let mut iter = vec.iter(); // 使用 iter 方法获取迭代器
// 使用 while let 循环遍历迭代器
while let Some(item) = iter.next() {
println!("Item: {}", item);
}
}
如果我们需要对向量中的元素进行可变的操作,可以使用 iter_mut
方法获取一个可变迭代器。
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
// 使用 iter_mut 方法获取可变迭代器
for item in vec.iter_mut() {
*item *= 2; // 修改元素
println!("Item: {}", item);
}
}
本例中,我们使用 iter_mut 方法获取向量 vec 的可变迭代器,并使用 for 循环遍历迭代器。在循环中,我们对每个元素进行了乘以 2 的操作,并打印出元素的值。
# 8. 关于 可变向量 与 不可变向量 说法的简要说明不可变向量是指在创建后不能修改其内容的向量。通过使用 let
关键字声明向量时,如果没有使用 mut
修饰符,那么该向量就是不可变向量。
请参考博文 《Rust 语言中的常量与变量》
可变向量是指在创建后可以修改其内容的向量。通过使用 let mut
关键字声明向量时,可以将其声明为可变向量。
请参考博文 《Rust 语言中的常量与变量》