Rust Notes: Using vectors in the Rust language

Rust notes
Using vector (vector) in Rust language

Author : Li Juncai (jcLee95) : https://blog.csdn.net/qq_28550263
Email: [email protected]
Address of this article : https://blog.csdn.net/qq_28550263/article/details/130876619


【介绍】:本文介绍 Rust 语言中的向量,包括向量与数组的区别,向量的相关API的用法等等。

Previous section: " Enumerations in the Rust Language " | Next section: " - "

Table of contents


1. Why do you need Vector

1.1 A brief review of Rust arrays

In Rust, an array is a fixed-size data structure whose length needs to be specified at compile time. It is very convenient to use arrays. In this section, we first review some basic features of arrays in the Rust language.

1.1.1 Arrays are stored contiguously in memory

When a contiguous memory space needs to be allocated on the stack, since the array is stored contiguously in the memory, it means that the storage of the elements is adjacent, and the elements can be quickly accessed through the index. This is useful in scenarios where fast, direct access to elements is required.

// 创建一个长度为5的整数数组
let array: [i32; 5] = [1, 2, 3, 4, 5];

// 访问数组元素
println!("第一个元素: {}", array[0]);
println!("第三个元素: {}", array[2]);

1.1.2 Arrays are length checked at compile time

When you need to know exactly the length of the array and want to check it at compile time, since Rust arrays can perform length checks at compile time, you can ensure that there will be no out-of-bounds access. This is used when it is necessary to ensure that the length of the array does not change, to prevent accidental errors.

// 创建一个长度为3的字符串数组
let array: [String; 3] = [
    String::from("Hello"),
    String::from("World"),
    String::from("Rust"),
];

// 访问数组元素
for element in &array {
    
    
    println!("{}", element);
}

It should be noted that the length of an array in Rust must be a compile- time constant , which means that the length of the array needs to be known exactly when it is created, and the size of the array cannot be dynamically changed at runtime . This static length limitation makes arrays not flexible enough to adapt to dynamic data collections in some cases.

1.2 Vector came into being

1.2.1 Defects of Rust arrays

While Rust arrays are useful in some situations, they can sometimes be inconvenient to use. First, because the length of a Rust array is determined at compile time and cannot be changed at runtime. This means that once the array is created, its size is fixed and cannot be dynamically adjusted according to actual needs . This limits the flexibility of arrays in scenarios where they need to grow or shrink dynamically. For example:

// 创建一个长度为3的整数数组
let array: [i32; 3] = [1, 2, 3];

// 无法动态改变数组长度
// array.push(4); // 错误!数组无法调用 `push` 方法

It is also because the length of the array is fixed, when you need to add more elements when the array is full, you need to manually reallocate the memory space and copy the existing data to the new array. This process requires manual management, adds coding complexity, and can result in a performance penalty. For example:

// 创建一个长度为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;

In this example, when we need to add more elements to the array, we need to manually reallocate the memory and copy the existing data to the new array. Not only is this tedious, it's also prone to introducing bugs and performance issues.

1.2.2 Overcoming Array Flaws: This is why Vector is needed

In order to overcome the various shortcomings of the array mentioned above, Rust provides the vector ( Vector ) type-this is a data structure that can dynamically change the size at runtime and automatically handle memory allocation and element movement. Vectors allow us to be more flexible and convenient when dealing with dynamically sized data sets.

2. Comparing arrays: Rust vector first experience

2.1 Dynamic size

The length of a vector can be changed dynamically at runtime, unlike an array that needs to specify a fixed length at compile time. This makes the vector more suitable for scenarios that need to grow or shrink dynamically, and can be automatically resized according to actual needs.

// 创建一个空的向量
let mut vector: Vec<i32> = Vec::new();

// 向向量添加元素
vector.push(1);
vector.push(2);
vector.push(3);

2.2 Automatic memory management

Vectors automatically handle memory allocation and movement of elements. When the length of the vector exceeds the currently allocated memory space, it automatically reallocates a larger memory space and copies the existing elements into the new memory space. This avoids the tedious work of manually managing memory and copying data, reducing the possibility of errors.

// 创建一个空的向量
let mut vector: Vec<i32> = Vec::new();

// 向向量添加元素
vector.push(1);
vector.push(2);
vector.push(3);
vector.push(4); // 超过当前内存空间,向量会自动重新分配更大的空间

// 自动处理内存分配和元素的移动
// 无需手动重新分配内存和复制数据

2.3 Flexible element types

Rust vectors can hold elements of different types and are not limited to a specific type. This makes vectors more flexible and can store and process various types of data.

// 创建一个包含不同类型元素的向量
let mut vector: Vec<dyn std::fmt::Debug> = Vec::new();
vector.push(1);
vector.push("hello");
vector.push(true);

2.4 Rich methods and functions

Rust vectors provide a rich set of methods and functions for manipulating and managing the elements of vectors. For example, you can use iterators to iterate over a vector, use len()to get the length of a vector, use contains()to check whether an element exists, etc.

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);

3. Vec structure

In the Rust language, Vec is a structure type, defined in the alloc::vec::Vec module of the standard library ( Structalloc::vec::Vec ), which represents a continuous growable array type, written as Vec , which is the abbreviation of " vector " . :

pub struct Vec<T, A: Allocator = Global> {
    
     /* private fields */ }

Please refer to the source code of Vec : https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#396-399 The following is a partial excerpt:

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)
    }
}
// ... ...

If you feel that the type of this vector cannot meet your needs, you can actually implement one yourself by referring to its code.

4. Choice: when to use arrays and when to use vectors

4.1 Vectors are also flawed

There are also shortcomings, just like a famous saying that I don't know the source, "When God closes a window, he opens another window for you." In this section we discuss some disadvantages of vectors compared to arrays in the Rust language.

4.1.1 Additional memory overhead

Since the vector is dynamically sized, it needs to allocate memory on the heap to store the elements, and also requires some additional memory space to manage and track the size and capacity of the vector. This leads to higher memory overhead for vectors compared to arrays.

4.1.2 Runtime performance overhead

Because the vector needs to dynamically allocate memory and move elements at runtime, this will bring a certain performance overhead. Operations on vectors (such as adding, deleting, resizing, etc.) may require reallocation of memory and copying of data, which may affect the performance of the program.

4.1.3 Memory Fragmentation

Vectors can cause fragmentation in the heap when dynamically resized. When the capacity of the vector no longer meets the demand and a larger memory space needs to be reallocated, if there is no continuous large block of available memory, memory copying and reallocation are required, which leads to the problem of memory fragmentation.

4.1.4 Other disadvantages

Limitations imposed by heap allocation

Elements of a vector are always stored on the heap and need to be accessed using pointers. This may introduce additional indirection overhead in some cases, especially when the number of elements is small and locality is high, and there may be some performance loss compared to stack allocation of arrays.

Unpredictable runtime errors

Since vectors are dynamically resized at runtime, memory allocation failures may occur. When a vector fails to allocate enough memory to store an element, it can cause runtime errors, which need to be handled appropriately.

4.2 Choose arrays and vectors according to different scenarios

After understanding the advantages and disadvantages of Rust vectors, the further need is how to choose according to their advantages and disadvantages. This section discusses how to choose between using arrays or vectors in different demand scenarios.

4.2.1 Critic factors for selection

In the Rust programming language, the choice between using an array or a vector depends on several considerations:

Fixed size or dynamic size:

  • Arrays are suitable for situations where the length can be determined at compile time. When it is necessary to allocate a continuous memory space on the stack, and the length is fixed and will not change, array is a suitable choice. For example, scenarios such as image buffers that store pixel data, or two-dimensional arrays that represent matrices.
  • Vectors are good for situations where the size changes dynamically at runtime. Vectors are a more flexible choice when the length needs to be dynamically adjusted according to actual needs, or when memory needs to be allocated on the heap and expanded and shrunk at runtime. For example, scenarios such as processing dynamic input data and dynamically generating data sets.

Memory management requirements:

  • The length of the array is determined at compile time, and the required memory space is also allocated at compile time, without dynamic memory management. This is useful for scenarios where memory usage needs to be explicitly controlled at compile time.
  • Vector automatically handles memory allocation and element movement, and can dynamically allocate and release memory according to actual needs. This is useful for scenarios that require dynamic memory management at runtime based on the size of the data collection.

Element type flexibility:

  • Arrays require that all elements must be of the same type and cannot accommodate data of different types. Arrays are the appropriate choice when you need to ensure that all elements have the same type. For example, scenarios such as storing sensor data of the same type.
  • Vectors can hold elements of different types, providing greater flexibility. Vectors are a more suitable choice when you need to store and process collections of different types of data. For example, processing data sets from different sensors, processing different types of configuration information, etc.

4.2.2 Concrete examples

An example of choosing an array

// 创建一个长度为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"),
];

In this example, arrays are used to store data collections of a specific size, such as pixel data and command-line arguments. These datasets already have a fixed size at compile time and do not need to be dynamically resized at runtime . Therefore, an array is a suitable choice because it provides a fixed-size data structure and memory allocation is determined at compile time.

An example of choosing a vector

// 创建一个空的整数向量,用于存储动态输入数据
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);

In this example, the size of the data set needs to be changed dynamically at runtime , such as dynamic input data and different types of data sets. Obviously, arrays do not have such capabilities, so we chose to use vectors.

All in all, if we can use arrays, we generally choose arrays to obtain higher performance. If the array cannot meet the requirements, we consider using vectors.

5. Method Analysis of Rust Vec

Note: You don’t need to memorize it, you just need to have an impression, and then remember some commonly used and simple methods such as pop, push, etc., and then come here to check when necessary.

5.1 Method overview

Here's how to Vecparse structs :

method name describe
allocator Returns the allocator used to allocate the Vecinternal buffer. The method returns Option<&A>, where Ais the type of allocator used to allocate memory.
append VecAppends all elements of one to another Vec. This method transfers ownership of elements Vecin to the target Vec.
as_mut_ptr Returns a mutable pointer to the first element of Vecbuffer .
as_mut_slice Returns a mutable slice that references the entire contents Vecof .
as_ptr Returns an immutable pointer to the first element of Vecthe buffer .
as_slice Returns an immutable slice that references the entire contents Vecof .
capacity Returns the Veccurrently allocated memory capacity in elements.
clear VecClears all elements in without freeing allocated memory.
dedup Remove adjacent repeated elements Vecin and keep only one of them.
dedup_by Removes adjacent duplicate elements Vecin .
dedup_by_key Removes adjacent duplicate elements Vecin .
drain Returns an iterator that removes and yields the specified range of elements from Vecthe .
drain_filter Returns an iterator that Vecremoves satisfy the specified criteria.
extend_from_slice Appends all elements of a slice Vecto the .
extend_from_within Creates and appends new elements from a subset of elements Vecin .
from_raw_parts Creates one from the given pointer and length Vec, which may be uninitialized.
from_raw_parts_in Creates one from the given pointer and length on the specified allocator Vec.
insert Inserts an element at the specified index position, and moves subsequent elements back one by one.
into_boxed_slice VecConverts to one Box<[T]>, transferring ownership Boxinto the .
into_flattened Flattens Vec<Vec<T>>a nested structure of into a single Vec<T>.
into_raw_parts VecConvert to a tuple (ptr, len, cap), preserving ownership.
into_raw_parts_with_alloc VecConvert to a tuple (ptr, len, cap, alloc), preserving ownership.
is_empty Checks Vecif is empty.
leak VecConverts to a raw pointer and prevents the destructor from being called.
len Returns the number of elements currently stored Vecin the .
new Create an empty one Vec.
new_in Create an empty one using the specified allocator Vec.
pop Removes and returns the last element Vecin .
push Appends an element to the end Vecof .
push_within_capacity Appends an element to the end Vecof , without allocating new memory if there is enough capacity.
remove Removes the element at the specified index position, and advances subsequent elements one by one.
reserve Increases the capacity Vecof the to accommodate the specified number of additional elements.
reserve_exact VecThe capacity of is increased to accommodate the specified number of additional elements, making the capacity exactly match.
resize Change the length Vecof the , possibly inserting default values ​​or removing elements.
resize_with Changes the length Vecof the , generating new elements using the specified closure.
retain Keep elements Vecin that , and remove elements that do not.
retain_mut Keeps mutable elements Vecin , removing elements that don't.
set_len Set the length Vecof , which can exceed the current capacity, but will produce uninitialized elements.
shrink_to VecShrinks the capacity of the to the specified size.
shrink_to_fit VecShrinks the capacity of the to the number of elements currently stored.
spare_capacity_mut Returns a mutable reference allowing direct access Vecto the unused buffer.
splice Replaces elements of another iterable with some of the elements Vecin .
split_at_spare_mut VecSplits into two mutable slices, one of which refers to an unused buffer.
split_off Vec 拆分为两个独立的 Vec,根据指定的索引位置。
swap_remove 移除指定索引位置的元素,并用最后一个元素替换它。
truncate Vec 截断为指定的长度。
try_reserve 尝试增加 Vec 的容量以容纳指定的附加元素数量。
try_reserve_exact 尝试增加 Vec 的容量以容纳指定的附加元素数量,使容量完全匹配。
with_capacity 使用指定的初始容量创建一个空的 Vec
with_capacity_in 使用指定的分配器和初始容量创建一个空的 Vec

5.2 容量和内存管理 相关方法

5.2.1 capacity 方法

该方法用于返回当前分配的内存容量(以元素为单位)。例如:

let vec = vec![1, 2, 3, 4, 5];
let capacity = vec.capacity();
println!("Capacity: {}", capacity);

5.2.2reserve 方法

该方法用于增加 Vec 的容量以容纳指定的附加元素数量。例如:

let mut vec = vec![1, 2, 3];
vec.reserve(3); // 增加容量以容纳3个额外元素
println!("Capacity after reserve: {}", vec.capacity());

5.2.3 reserve_exact 方法

该方法用于增加 Vec 的容量以容纳指定的附加元素数量,使容量完全匹配。例如:

let mut vec = vec![1, 2, 3];
vec.reserve_exact(4); // 增加容量以容纳4个额外元素
println!("Capacity after reserve_exact: {}", vec.capacity());

5.2.4 shrink_to 方法

该方法用于将 Vec 的容量收缩到指定的大小。例如:

let mut vec = vec![1, 2, 3, 4, 5];
vec.shrink_to(3); // 将容量收缩到3
println!("Capacity after shrink_to: {}", vec.capacity());

5.2.5 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());

5.2.6 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);

5.2.7 with_capacity 方法:

该方法用于使用指定的初始容量创建一个空的 Vec。例如:

let vec: Vec<u32> = Vec::with_capacity(5);
println!("Capacity: {}", vec.capacity());

5.2.8 with_capacity_in 方法

该方法用于使用指定的分配器和初始容量创建一个空的 Vec。例如:

use std::alloc::System;
let vec: Vec<u32, System> = Vec::with_capacity_in(5);
println!("Capacity: {}", vec.capacity());

这些方法提供了对 Vec 容量和内存管理的功能,可以根据需求调整容量、预留内存或收缩容量,以优化内存使用和性能。

5.3 元素访问和操作 相关方法

5.3.1 as_mut_ptr 方法

该方法返回一个可变指针,指向 Vec 的第一个元素。例如:

let mut vec = vec![1, 2, 3];
let ptr = vec.as_mut_ptr();
unsafe {
    
    
    *ptr = 5; // 修改第一个元素的值
}
println!("Modified Vec: {:?}", vec);

5.3.2 as_mut_slice 方法

该方法返回一个可变切片,包含整个 Vec 的元素。例如:

let mut vec = vec![1, 2, 3];
let slice = vec.as_mut_slice();
slice[0] = 5; // 修改第一个元素的值
println!("Modified Vec: {:?}", vec);

5.3.3 as_ptr 方法

该方法返回一个不可变指针,指向 Vec 的第一个元素。例如:

let vec = vec![1, 2, 3];
let ptr = vec.as_ptr();
unsafe {
    
    
    println!("First element: {}", *ptr);
}

5.3.4 as_slice 方法

该方法用于返回一个不可变切片,包含整个 Vec 的元素。例如:

let vec = vec![1, 2, 3];
let slice = vec.as_slice();
println!("Slice: {:?}", slice);

5.3.5 get 方法

该方法用于根据索引获取 Vec 中的元素的不可变引用。例如:

let vec = vec![1, 2, 3];
if let Some(element) = vec.get(1) {
    
    
    println!("Second element: {}", element);
} else {
    
    
    println!("Element not found");
}

5.3.6 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);

5.3.7 insert 方法

该方法用于在指定索引位置插入一个元素,并将后续元素依次后移。例如:

let mut vec = vec![1, 2, 3];
vec.insert(1, 4); // 在索引1插入元素4
println!("Modified Vec: {:?}", vec);

5.3.8 pop 方法

该方法用于移除并返回 Vec 中的最后一个元素。例如:

let mut vec = vec![1, 2, 3];
if let Some(element) = vec.pop() {
    
    
    println!("Popped element: {}", element);
} else {
    
    
    println!("Vec is empty");
}

5.3.9 push 方法

该方法用于在 Vec 的末尾添加一个元素。例如:

let mut vec = vec![1, 2, 3];
vec.push(4); // 在末尾添加元素4
println!("Modified Vec: {:?}", vec);

5.3.10 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);

5.3.11 remove 方法

该方法用于移除指定索引处的元素,并将后续元素依次前移。例如:

let mut vec = vec![1, 2, 3];
let removed = vec.remove(1); // 移除索引1处的元素
println!("Removed element: {}", removed);
println!("Modified Vec: {:?}", vec);

5.3.12 swap 方法

该方法用于交换 Vec 中两个索引位置的元素。例如:

let mut vec = vec![1, 2, 3];
vec.swap(0, 2); // 交换索引0和索引2处的元素
println!("Modified Vec: {:?}", vec);

5.3.13 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 中的元素。

5.4 元素迭代和遍历 相关方法

5.4.1 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);

5.4.2 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);

5.4.3 iter 方法

该方法返回一个不可变迭代器,用于按顺序访问 Vec 的元素。例如:

let vec = vec![1, 2, 3, 4, 5];
for element in vec.iter() {
    
    
    println!("Element: {}", element);
}

5.4.4 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 的元素进行迭代和操作。

5.5 元素修改和变换 相关方法

5.5.1 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);

5.5.2 clear 方法

该方法移除 Vec 的所有元素,将其长度设置为 0。例如:

let mut vec = vec![1, 2, 3];
vec.clear(); // 移除所有元素
println!("Cleared Vec: {:?}", vec);

5.5.3 dedup 方法

该方法移除 Vec 中连续出现的重复元素,只保留一个副本。例如:

let mut vec = vec![1, 2, 2, 3, 3, 3, 4, 5];
vec.dedup(); // 移除连续出现的重复元素
println!("Modified Vec: {:?}", vec);

5.5.4 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);

5.5.5 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);

5.5.6 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);

5.5.7 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);

5.5.8 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);

5.5.9 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);

5.5.10 resize 方法

该方法修改 Vec 的长度,将其扩展或收缩到指定的大小,并使用给定的值填充新元素。例如:

let mut vec = vec![1, 2, 3];
vec.resize(5, 0); // 扩展 Vec 的长度到5,并用0填充新元素
println!("Modified Vec: {:?}", vec);

5.5.11 resize_with 方法

该方法修改 Vec 的长度,将其扩展或收缩到指定的大小,并使用提供的闭包生成新元素。例如:

let mut vec = vec![1, 2, 3];
vec.resize_with(5, Default::default); // 扩展 Vec 的长度到5,并使用默认值生成新元素
println!("Modified Vec: {:?}", vec);

5.5.12 retain 方法

该方法根据指定的条件保留 Vec 中满足条件的元素,移除不满足条件的元素。例如:

let mut vec = vec![1, 2, 3, 4, 5];
vec.retain(|&x| x % 2 == 0); // 保留偶数元素
println!("Modified Vec: {:?}", vec);

5.5.13 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);

5.5.14 set_len 方法

该方法修改 Vec 的长度,将其设置为指定的长度,但不改变底层的内存分配。例如:

let mut vec = vec![1, 2, 3, 4, 5];
vec.set_len(3); // 设置 Vec 的长度为3
println!("Modified Vec: {:?}", vec);

5.5.15 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);

5.6 状态和属性查询 相关方法

5.6.1 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);

5.6.2 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

5.6.3 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 的有关信息,并进行相应的处理。

5.7 其它方法

5.7.1 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);

5.7.2 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);

5.7.3 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);

5.7.4 into_flattened 方法

该方法将 Vec<Vec<T>> 转换为一个 Vec<T>,将内部的嵌套 Vec 展平。例如:

let vec = vec![vec![1, 2], vec![3, 4, 5], vec![6]];
let flattened: Vec<i32> = vec.into_flattened();
println!("Flattened Vec: {:?}", flattened);

5.7.5 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);

5.7.6 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);

5.7.7 leak 方法

该方法将 Vec 转换为一个静态生命周期的引用,并且不会执行内存释放。例如:

let vec: Vec<i32> = vec![1, 2, 3];
let leaked: &'static mut [i32] = Vec::leak(vec);
println!("Leaked slice: {:?}", leaked);

5.7.8 truncate 方法

该方法修改 Vec 的长度,截断到指定的长度,丢弃超过长度的元素。例如:

let mut vec = vec![1, 2, 3, 4, 5];
vec.truncate(3); // 截断 Vec 的长度为3
println!("Truncated Vec: {:?}", vec);

5.7.9 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");
}

5.7.10 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");
}

6. 关于 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 宏在编译时会自动计算向量的容量,并确保分配足够的内存来存储初始值。这使得在创建向量时不需要手动指定容量,而是由编译器根据提供的初始值进行计算。这样可以提供更高的代码灵活性和可读性,同时减少了手动计算容量的繁琐工作。

注意:

7. Rust 向量的遍历

7.1 使用 for 循环遍历向量

我们可以使用 for 循环来遍历向量中的元素,例如:

fn main() {
    
    
    let vec = vec![1, 2, 3, 4, 5];

    for item in &vec {
    
    
        println!("Item: {}", item);
    }
}

在上面的例子中,我们使用 for 循环遍历向量 vec 中的每个元素,并打印出元素的值。

不过需要指出的是,在 Rust 语言中使用 for 循环进行遍历的方式是一种语法糖,它隐藏了不过迭代器的使用。因此读者也可以参考使用迭代器的遍历方式。

7.2 迭代器

7.2.1 迭代器的概念

在 Rust 中,迭代器(Iterator)是一种序列的抽象,它提供了一种统一的方式来遍历和处理序列中的元素。迭代器是 Rust 标准库中的一个重要组件,广泛应用于向量、哈希表、文件、字符串等数据结构和类型。

Iterators implement a Iterator trait that defines a set of methods for manipulating and processing elements in a sequence. By using iterators, we can handle different types of sequences in a unified way, whether it is an array, vector, hash table, or file, etc. This design makes the code more expressive and flexible, while also providing better performance and safety.

7.2.2 Obtaining an iterator from a vector

1. Use the iter method to get an iterator

If we need to perform variable operations on the elements in the vector, we can use the iter_mut method to obtain a variable iterator. For example:

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);
    }
}

2. Use the iter_mut method to get an iterator

If we need to perform mutable operations on the elements in the vector, we can use iter_mutthe method to get a mutable iterator.

fn main() {
    
    
    let mut vec = vec![1, 2, 3, 4, 5];

    // 使用 iter_mut 方法获取可变迭代器
    for item in vec.iter_mut() {
    
    
        *item *= 2; // 修改元素
        println!("Item: {}", item);
    }
}

In this example, we use the iter_mut method to obtain a mutable iterator to the vector vec, and use a for loop to iterate over the iterator. In the loop, we multiply each element by 2 and print out the value of the element.

# 8. A brief explanation of the mutable and immutable vectors

8.1 Immutable vectors

Immutable vectors are vectors whose contents cannot be modified after creation. When declaring a vector using letthe keyword without mutthe modifier, the vector is an immutable vector.

Please refer to the blog post "Constants and Variables in Rust Language"

8.2 Mutable vectors

A mutable vector is one whose contents can be modified after creation. When a vector is declared by using let mutthe keyword , it can be declared as a mutable vector.

Please refer to the blog post "Constants and Variables in Rust Language"

Guess you like

Origin blog.csdn.net/qq_28550263/article/details/130876619