Rust ノート: Rust 言語でのベクトルの使用

錆びたメモ
Rust言語でベクトル(vector)を使う

著者: Li Juncai (jcLee95) : https://blog.csdn.net/qq_28550263
電子メール : [email protected]
この記事のアドレス: https://blog.csdn.net/qq_28550263/article/details/130876619


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

前のセクション: " 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このメソッドは、元VecVec
as_mut_ptr Vecbufferの最初の要素への可変ポインタを返します。
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 Vec1 に変換し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 Vec2 つの可変スライスに分割され、そのうちの 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 宏在编译时会自动计算向量的容量,并确保分配足够的内存来存储初始值。这使得在创建向量时不需要手动指定容量,而是由编译器根据提供的初始值进行计算。这样可以提供更高的代码灵活性和可读性,同时减少了手动计算容量的繁琐工作。

注意:

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

ブログ投稿「Rust 言語の定数と変数」を参照してください。

8.2 可変ベクトル

可変ベクトルとは、作成後にその内容を変更できるベクトルです。let mutキーワードを使用してベクトルを宣言すると、可変ベクトルとして宣言できます。

ブログ投稿「Rust 言語の定数と変数」を参照してください。

おすすめ

転載: blog.csdn.net/qq_28550263/article/details/130876619