著者: Li Juncai (jcLee95) : https://blog.csdn.net/qq_28550263
電子メール : [email protected]
この記事のアドレス: https://blog.csdn.net/qq_28550263/article/details/130876619
【介绍】:本文介绍 Rust 语言中的向量,包括向量与数组的区别,向量的相关API的用法等等。
目次
- 1. ベクターが必要な理由
- 2. 配列の比較: Rust Vector の初体験
- 3. Vec構造
- 4. 選択: 配列をいつ使用するか、いつベクトルを使用するか
- 5. Rust Vecのメソッド分析
-
- 5.1 手法の概要
- 5.2 容量およびメモリ管理関連の方法
- 5.3 要素へのアクセスと操作関連メソッド
- 5.4 要素の反復と走査に関連するメソッド
- 5.5 要素の変更と変換関連のメソッド
-
- 5.5.1 `append` 方法</a>
- 5.5.2 `clear` 方法</a>
- 5.5.3 `dedup` メソッド</a>
- 5.5.4 `dedup_by` 方法</a>
- 5.5.5 `dedup_by_key` メソッド</a>
- 5.5.6 `extend` メソッド</a>
- 5.5.7 `extend_from_slice` 方法</a>
- 5.5.8 `extend_from_within` メソッド</a>
- 5.5.9 `replace` 方法</a>
- 5.5.10 `resize` メソッド</a>
- 5.5.11 `resize_with` 方法</a>
- 5.5.12 `retain` メソッド</a>
- 5.5.13 `retain_mut` メソッド</a>
- 5.5.14 `set_len` メソッド</a>
- 5.5.15 `splice` メソッド</a>
- 5.6 ステータスおよび属性クエリ関連のメソッド
- 5.7 その他の方法
-
- 5.7.1 `from_raw_parts` メソッド</a>
- 5.7.2 `from_raw_parts_in` メソッド</a>
- 5.7.3 `into_boxed_slice` 方法</a>
- 5.7.4 `into_flattened` 方法</a>
- 5.7.5 `into_raw_parts` メソッド</a>
- 5.7.6 `into_raw_parts_with_alloc` メソッド</a>
- 5.7.7 「リーク」メソッド</a>
- 5.7.8 `truncate` メソッド</a>
- 5.7.9 `try_reserve` 方法</a>
- 5.7.10 `try_reserve_exact` 方法</a>
- 6. `alloc::vec` マクロについて
- 7. Rust ベクトルトラバーサル
1. ベクターが必要な理由
1.1 Rust配列の簡単なレビュー
Rust では、配列は固定サイズのデータ構造であり、その長さはコンパイル時に指定する必要があります。配列を使用すると非常に便利です。このセクションでは、まずRust言語における配列の基本的な機能をいくつか確認します。
1.1.1 配列はメモリ内に連続して格納される
連続したメモリ空間をスタック上に割り当てる必要がある場合、配列はメモリ内に連続して格納されるため、要素の格納場所が隣接していることになり、インデックスを介して要素に迅速にアクセスできます。これは、要素への高速かつ直接アクセスが必要なシナリオで役立ちます。
// 创建一个长度为5的整数数组
let array: [i32; 5] = [1, 2, 3, 4, 5];
// 访问数组元素
println!("第一个元素: {}", array[0]);
println!("第三个元素: {}", array[2]);
1.1.2 配列の長さはコンパイル時にチェックされます
配列の長さを正確に知る必要があり、コンパイル時にチェックしたい場合、Rust配列はコンパイル時に長さチェックを実行できるため、範囲外アクセスがないことを保証できます。これは、偶発的なエラーを防ぐために、配列の長さが変わらないことを保証する必要がある場合に使用されます。
// 创建一个长度为3的字符串数组
let array: [String; 3] = [
String::from("Hello"),
String::from("World"),
String::from("Rust"),
];
// 访问数组元素
for element in &array {
println!("{}", element);
}
Rust の配列の長さはコンパイルこれは、配列の長さを作成時に正確に知る必要があり、配列のサイズは実行時に動的に変更できないことを意味します。。この静的な長さの制限により、場合によっては配列が動的データ収集に適応できないほど柔軟性が低くなります。
1.2 ベクターの誕生
1.2.1 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;
この例では、配列にさらに要素を追加する必要がある場合、手動でメモリを再割り当てし、既存のデータを新しい配列にコピーする必要があります。これは面倒なだけでなく、バグやパフォーマンスの問題が発生する傾向があります。
1.2.2 配列の欠陥の克服: これが Vector が必要な理由です
上で述べた配列のさまざまな欠点を克服するために、Rust はベクトル( Vector ) 型を提供します。これは、実行時にサイズを動的に変更し、メモリ割り当てと要素の移動を自動的に処理できるデータ構造です。ベクトルを使用すると、動的にサイズ変更されるデータ セットを扱う際に、より柔軟で便利になります。
2. 配列の比較: Rust Vector の初体験
2.1 動的サイズ
コンパイル時に固定長を指定する必要がある配列とは異なり、ベクトルの長さは実行時に動的に変更できます。これにより、ベクトルは動的に拡大または縮小する必要があるシナリオにより適しており、実際のニーズに応じて自動的にサイズ変更できます。
// 创建一个空的向量
let mut vector: Vec<i32> = Vec::new();
// 向向量添加元素
vector.push(1);
vector.push(2);
vector.push(3);
2.2 自動メモリ管理
ベクターは、メモリの割り当てと要素の移動を自動的に処理します。ベクトルの長さが現在割り当てられているメモリ空間を超えると、より大きなメモリ空間が自動的に再割り当てされ、既存の要素が新しいメモリ空間にコピーされます。これにより、手動でメモリを管理し、データをコピーするという面倒な作業が回避され、エラーの可能性が減ります。
// 创建一个空的向量
let mut vector: Vec<i32> = Vec::new();
// 向向量添加元素
vector.push(1);
vector.push(2);
vector.push(3);
vector.push(4); // 超过当前内存空间,向量会自动重新分配更大的空间
// 自动处理内存分配和元素的移动
// 无需手动重新分配内存和复制数据
2.3 柔軟な要素タイプ
Rustベクターはさまざまなタイプの要素を保持でき、特定のタイプに限定されません。これにより、ベクトルがより柔軟になり、さまざまな種類のデータを保存および処理できるようになります。
// 创建一个包含不同类型元素的向量
let mut vector: Vec<dyn std::fmt::Debug> = Vec::new();
vector.push(1);
vector.push("hello");
vector.push(true);
2.4 豊富なメソッドと機能
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);
3. Vec構造
Rust言語では、Vecは標準ライブラリ ( Struct ) のalloc::vec::Vecモジュールで定義された構造体型であり、連続拡張可能な配列型を表し、 「 Vector 」の略語であるVecとして記述されます。 。:alloc::vec::Vec
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)
}
}
// ... ...
このベクトルの型ではニーズを満たせないと思われる場合は、そのコードを参照して実際に自分で実装できます。
4. 選択: 配列をいつ使用するか、いつベクトルを使用するか
4.1 ベクトルにも欠陥がある
出典は分かりませんが「神が窓を閉めると、また別の窓を開けてくれる」という出典は分からない有名な言葉のように、欠点もあります。このセクションでは、Rust言語の配列と比較したベクトルのいくつかの欠点について説明します。
4.1.1 追加のメモリオーバーヘッド
ベクトルのサイズは動的に変更されるため、要素を格納するためにヒープ上にメモリを割り当てる必要があり、また、ベクトルのサイズと容量を管理および追跡するために追加のメモリ スペースも必要になります。これにより、配列に比べてベクトルのメモリ オーバーヘッドが高くなります。
4.1.2 実行時のパフォーマンスのオーバーヘッド
ベクターは実行時に動的にメモリを割り当て、要素を移動する必要があるため、パフォーマンスに一定のオーバーヘッドが生じます。ベクトルに対する操作 (追加、削除、サイズ変更など) では、メモリの再割り当てやデータのコピーが必要になる場合があり、プログラムのパフォーマンスに影響を与える可能性があります。
4.1.3 メモリの断片化
ベクターのサイズを動的に変更すると、ヒープ内で断片化が発生する可能性があります。ベクターの容量が要求を満たさなくなり、より大きなメモリ空間を再割り当てする必要がある場合、使用可能なメモリの大きなブロックが連続して存在しない場合、メモリのコピーと再割り当てが必要となり、メモリの断片化の問題が発生します。
4.1.4 その他の欠点
ヒープ割り当てによる制限
ベクターの要素は常にヒープ、ポインターを使用してアクセスする必要があります。これにより、場合によっては追加の間接オーバーヘッドが発生する可能性があり、特に要素数が少なく局所性が高い場合には、配列のスタック割り当てと比較してパフォーマンスが低下する可能性があります。
予測できない実行時エラー
ベクトルは実行時に動的にサイズ変更されるため、メモリ割り当てエラーが発生する可能性があります。ベクターが要素を保存するのに十分なメモリを割り当てられない場合、実行時エラーが発生する可能性があるため、適切に処理する必要があります。
4.2 さまざまなシナリオに応じて配列とベクトルを選択する
Rust ベクターの長所と短所を理解した後は、長所と短所に応じてどのように選択するかがさらに必要になります。このセクションでは、さまざまな需要シナリオで配列とベクトルのどちらを使用するかを選択する方法について説明します。
4.2.1 選択の重要な要素
Rust プログラミング言語では、配列を使用するかベクトルを使用するかの選択は、いくつかの考慮事項によって決まります。
固定サイズまたは動的サイズ:
- 配列は、コンパイル時に長さを決定できる状況に適しています。スタック上に連続したメモリ空間を割り当てる必要があり、長さが固定されており変更されない場合には、配列が適しています。たとえば、ピクセル データを格納する画像バッファや行列を表す 2 次元配列などのシナリオです。
- ベクターは、実行時にサイズが動的に変化する状況に適しています。実際のニーズに応じて長さを動的に調整する必要がある場合、またはメモリをヒープに割り当てて実行時に拡張および縮小する必要がある場合、ベクトルはより柔軟な選択肢となります。たとえば、動的入力データの処理やデータ セットの動的生成などのシナリオです。
メモリ管理要件:
- 配列の長さはコンパイル時に決定され、必要なメモリ空間も動的メモリ管理なしでコンパイル時に割り当てられます。これは、コンパイル時にメモリ使用量を明示的に制御する必要があるシナリオに役立ちます。
- Vector はメモリ割り当てと要素の移動を自動的に処理し、実際のニーズに応じてメモリを動的に割り当てたり解放したりできます。これは、データ コレクションのサイズに基づいて実行時に動的なメモリ管理を必要とするシナリオに役立ちます。
要素タイプの柔軟性:
- 配列では、すべての要素が同じ型である必要があり、異なる型のデータを収容することはできません。すべての要素が同じ型であることを確認する必要がある場合、配列は適切な選択肢です。たとえば、同じ種類のセンサー データを保存するようなシナリオです。
- ベクターはさまざまなタイプの要素を保持できるため、柔軟性が高まります。さまざまな種類のデータのコレクションを保存および処理する必要がある場合は、ベクターの方が適しています。たとえば、さまざまなセンサーからのデータセットの処理、さまざまな種類の構成情報の処理などです。
4.2.2 具体例
配列の選択例
// 创建一个长度为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);
この例では、動的入力データやさまざまなタイプのデータ セットなど、データ セットのサイズを実行時に動的に変更する必要があります。明らかに、配列にはそのような機能がないため、ベクトルを使用することにしました。
全体として、配列を使用できる場合、通常はより高いパフォーマンスを得るために配列を選択します。配列が要件を満たせない場合は、ベクトルの使用を検討します。
5. Rust Vecのメソッド分析
注: 暗記する必要はありません。印象を持って、ポップ、プッシュなどのよく使われる簡単な方法を覚えて、必要なときにここに来て確認するだけで十分です。
5.1 手法の概要
Rust 言語でVec
構造体を 1 つずつ解析する。
メソッド名 | 説明 |
---|---|
allocator |
Vec 内部バッファを割り当てるために使用されるアロケータを返します。メソッドは を返しますOption<&A> 。ここで、 はメモリの割り当てに使用されるアロケータのタイプA です。 |
append |
あるVec のすべての要素を別の に追加しますVec 。このメソッドは、元Vec のVec 。 |
as_mut_ptr |
Vec bufferの最初の要素への可変ポインタを返します。 |
as_mut_slice |
Vec の内容全体を参照する可変スライスを返します。 |
as_ptr |
Vec バッファの最初の要素への不変ポインタを返します。 |
as_slice |
Vec の内容全体を参照する不変スライスを返します。 |
capacity |
Vec 現在割り当てられているメモリ容量を要素単位で返します。 |
clear |
Vec 割り当てられたメモリを解放せずに、すべての要素をクリアします。 |
dedup |
隣接する繰り返し要素Vec を、そのうちの 1 つだけを保持します。 |
dedup_by |
指定された比較クロージャを使用してVec 、。 |
dedup_by_key |
指定されたキー抽出関数を使用してVec 、。 |
drain |
Vec から指定された範囲の要素を削除して生成する反復子を返します。 |
drain_filter |
指定された基準を満たす要素Vec を削除し。 |
extend_from_slice |
スライスのすべての要素Vec を に。 |
extend_from_within |
指定されたクロージャを使用しVec て、新しい要素を作成して追加します。 |
from_raw_parts |
指定されたポインターと length から 1 つを作成しますVec が、初期化されていない可能性があります。 |
from_raw_parts_in |
指定されたアロケータ上の指定されたポインタと長さから 1 つを作成しますVec 。 |
insert |
指定されたインデックス位置に要素を挿入し、後続の要素を 1 つずつ戻します。 |
into_boxed_slice |
Vec 1 に変換しBox<[T]> 、所有権Box を に。 |
into_flattened |
Vec<Vec<T>> の入れ子構造を単一の に平坦化しますVec<T> 。 |
into_raw_parts |
Vec 所有権を維持して tuple に変換します(ptr, len, cap) 。 |
into_raw_parts_with_alloc |
Vec 所有権を維持して tuple に変換します(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 |
指定されたインデックス位置にある要素を削除し、後続の要素を 1 つずつ進めます。 |
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 2 つの可変スライスに分割され、そのうちの 1 つは未使用のバッファーを参照します。 |
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
宏在编译时会自动计算向量的容量,并确保分配足够的内存来存储初始值。这使得在创建向量时不需要手动指定容量,而是由编译器根据提供的初始值进行计算。这样可以提供更高的代码灵活性和可读性,同时减少了手动计算容量的繁琐工作。
注意:
alloc::vec
宏位于alloc
模块中,而不是标准库的根模块中。因此在使用之前需要通过use
语句将其导入到作用域中,即:use alloc::vec;
- 更多细节可以参考:https://doc.rust-lang.org/src/alloc/macros.rs.html#42-57
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 标准库中的一个重要组件,广泛应用于向量、哈希表、文件、字符串等数据结构和类型。
イテレータは、シーケンス内の要素を操作および処理するための一連のメソッドを定義する特性Iterator
を実装します。イテレータを使用すると、配列、ベクトル、ハッシュ テーブル、ファイルなど、さまざまな種類のシーケンスを統一した方法で処理できます。この設計により、コードの表現力と柔軟性が向上すると同時に、パフォーマンスと安全性も向上します。
7.2.2 ベクトルからイテレータを取得する
1. iterメソッドを使用してイテレータを取得します
ベクトル内の要素に対して変数演算を実行する必要がある場合は、 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);
}
}
2. iter_mutメソッドを使用してイテレータを取得します
ベクトル内の要素に対して変更可能な操作を実行する必要がある場合は、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. 可変ベクトルと不変ベクトルの簡単な説明8.1 不変ベクトル
不変ベクトルとは、作成後に内容を変更できないベクトルです。修飾子なしでlet
キーワードを使用してベクトルを宣言すると、そのベクトルは不変ベクトルになります。mut
8.2 可変ベクトル
可変ベクトルとは、作成後にその内容を変更できるベクトルです。let mut
キーワードを使用してベクトルを宣言すると、可変ベクトルとして宣言できます。