404 Not Found

404 Not Found


nginx

Rustのベクトル(Vec):動的配列の作成、削除、変更、および照会

Vec<T> (vector) は、Rust 標準ライブラリで最も一般的に使用される動的配列です。実行時にサイズを拡大・縮小することができ、すべてのデータはヒープ上に割り当てられます。これは、Rust における「ArrayList」や「Python のリスト」に相当するものです。

配列が「固定サイズのロッカー」のようなものだとすれば、Vecは「拡張可能な倉庫」のようなものです。スペースが足りなくなると自動的に拡張されるため、手動でメモリを管理する必要がありません。


1. 学習内容


2. スーパーのレジ係の物語

(1) もどかしさ:ショッピングカートの大きさがわからないこと

トムはスーパーマーケットのレジシステムを開発しています。顧客の買い物かごには:

RUST
// The problem with arrays -- you must know how many items in advance!
let cart: [&str; 3] = ["milk", "bread", "eggs"];

// Customer says: "Add one more cola"
// Arrays can't grow dynamically -- you need to redeclare...
let mut cart2 = ["milk", "bread", "eggs", "cola"];
// Customer: "Remove eggs, add butter instead"
// Modify every time? This is painful!

配列のサイズはコンパイル時に固定されます。しかし、ショッピングカートの場合、顧客が3個買うのか30個買うのかはわかりません。そのため、実行時に動的に拡張できるデータ構造が必要となります。

(2) RustのVecに対する解決策

RUST
fn main() {
    // Start with an empty cart
    let mut cart: Vec<&str> = Vec::new();

    // Customer adds items one by one
    cart.push("milk");
    cart.push("bread");
    cart.push("eggs");
    println!("Cart has {} items: {:?}", cart.len(), cart);

    // Customer adds more
    cart.push("cola");
    println!("Added cola: {:?}", cart);

    // Customer removes an item
    cart.pop();
    println!("Removed last: {:?}", cart);

    // Check what's inside
    println!("Current cart: {:?}", cart);
}

Vec::new() 空の動的配列を作成します。push 要素を追加します(配列のサイズは自動的に調整されます)。pop 最後の要素を削除します。サイズを指定する必要はありません。Vec はヒープ上のメモリを自動的に管理します。


3. 動的配列 Vec

(1) コンセプトの概要

100%
graph LR
    A[Vec&lt;T&gt; dynamic array] --> B[Creation]
    A --> C[CRUD operations]
    A --> D[Capacity management]
    A --> E[Conversion]

    B --> B1[Vec::new()]
    B --> B2[vec! macro]
    B --> B3[collect()]

    C --> C1[push / pop]
    C --> C2[insert / remove]
    C --> C3[index / .get()]

    D --> D1[capacity: allocated]
    D --> D2[len: actual use]
    D --> D3[shrink_to_fit]

    E --> E1[From array]
    E --> E2[Back to array]

(2) ベクトルと配列の比較

次元 配列 [T; N] ベクトル Vec<T>
サイズ コンパイル時に固定 実行時に動的に増加
割り当て場所 スタック(通常) ヒープ
拡大・縮小 未対応 自動で2倍に拡大
APIの充実度 限定的 極めて充実
アクセス速度 極めて高速(スタック上で連続) 極めて高速(ヒープ上で連続)
ユースケース 固定サイズ、スタック上に割り当てられる 数が不定、頻繁な追加・削除
パフォーマンス上のオーバーヘッド なし push により再割り当てが発生する可能性がある

(3) 一般的なVecメソッドのクイックリファレンス

メソッド 戻り値の型 説明 時間計算量
push(val) () 末尾への追加 O(1)(平均時間)
pop() Option<T> テール・ポップアップ O(1)
insert(idx, val) () 指定した位置への挿入 O(n)
remove(idx) T 指定した位置で削除 O(n)
get(idx) Option<&T> セキュアアクセス O(1)
len() usize 要素の数 O(1)
capacity() usize 割り当て容量 O(1)
clear() () O(n) でクリア
contains(&val) bool 含む O(n)
sort() () インプレースソート O(n log n)
dedup() () 重複を削除(事前にソートが必要) O(n)
retain(f) () 条件を満たす要素を保持する O(n)
shrink_to_fit() () 余剰容量を解放する

(4) ベクトル反復法の比較

メソッド 構文 所有権 Vec で利用可能
借用された反復 for x in &v 読み取り専用参照 利用可能
可変引数によるトラバーサル for x in &mut v 可変引数による参照 利用可能(修正済み)
消費によるトラバーサル for x in v 所有権の移転 利用不可
イテレータ v.iter() 読み取り専用参照 利用可能
列挙の反復処理 v.iter().enumerate() 読み取り専用参照 + インデックス 利用可能

4. 例

(1) ▶ サンプル:vec! マクロと push/pop (難易度 ⭐)

RUST
// ============================================
// Vec: vec! macro, push, pop, len, is_empty
// ============================================

fn main() {
    // Method 1: Vec::new()
    let mut v1: Vec<i32> = Vec::new();
    v1.push(10);
    v1.push(20);
    v1.push(30);
    println!("v1: {:?}, len={}", v1, v1.len());

    // Method 2: vec! macro (most common)
    let mut v2 = vec!["apple", "banana", "cherry"];
    println!("v2: {:?}, len={}", v2, v2.len());

    // Method 3: vec! with repeated value
    let v3 = vec![0; 5];
    println!("v3 (five zeros): {:?}", v3);

    // push: add to the end
    v2.push("date");
    println!("After push: {:?}", v2);

    // pop: remove from the end
    let last = v2.pop();
    println!("Popped: {:?}", last);
    println!("After pop: {:?}", v2);

    // is_empty
    println!("v2 empty? {}", v2.is_empty());

    // Access by index
    println!("v2[0] = {}", v2[0]);

    // len
    println!("v2 length: {}", v2.len());
}

出力:

TEXT
v1: [10, 20, 30], len=3
v2: ["apple", "banana", "cherry"], len=3
v3 (five zeros): [0, 0, 0, 0, 0]
After push: ["apple", "banana", "cherry", "date"]
Popped: Some("date")
After pop: ["apple", "banana", "cherry"]
v2 empty? false
v2[0] = apple
v2 length: 3

vec! はリストを作成する最も一般的な方法です。リストには vec!["a", "b", "c"] または vec![0; 5](0が5つ)を指定します。push はリストの末尾に要素を追加し、pop はリストの末尾から要素を取り出します(Option<T> を返します)。len() はリストに含まれる要素の数を返します。


(2) ▶ サンプル:挿入・削除と容量管理(難易度 ⭐⭐)

RUST
// ============================================
// Vec: insert, remove, capacity vs length
// ============================================

fn main() {
    let mut cart: Vec<&str> = Vec::with_capacity(3);

    // capacity vs length
    println!("Initial -- len: {}, cap: {}", cart.len(), cart.capacity());

    cart.push("milk");
    cart.push("bread");
    cart.push("eggs");
    println!("After 3 pushes -- len: {}, cap: {}", cart.len(), cart.capacity());

    // insert at arbitrary position
    cart.insert(1, "cola");  // insert "cola" at index 1
    println!("After insert at 1: {:?}", cart);
    println!("Now -- len: {}, cap: {}", cart.len(), cart.capacity());
    // Capacity may have doubled!

    // remove at arbitrary position
    let removed = cart.remove(2);  // remove element at index 2
    println!("Removed: {}", removed);
    println!("After remove at 2: {:?}", cart);

    // get -- safe access (returns Option<&T>)
    match cart.get(0) {
        Some(item) => println!("First item: {}", item),
        None => println!("Empty cart!"),
    }

    // Try an out-of-bounds index with get (safe)
    match cart.get(99) {
        Some(item) => println!("Item at 99: {}", item),
        None => println!("Index 99 is out of bounds!"),
    }

    // shrink_to_fit: reduce capacity to match length
    cart.shrink_to_fit();
    println!("After shrink -- len: {}, cap: {}", cart.len(), cart.capacity());
}

出力:

TEXT
Initial -- len: 0, cap: 3
After 3 pushes -- len: 3, cap: 3
After insert at 1: ["milk", "cola", "bread", "eggs"]
Now -- len: 4, cap: 6
Removed: bread
After remove at 2: ["milk", "cola", "eggs"]
First item: milk
Index 99 is out of bounds!
After shrink -- len: 3, cap: 3

insert(idx, val) は指定された位置に挿入し(後続の要素を右にずらす)、remove(idx) は指定された位置の要素を削除してそれを返す。capacity は Vec に割り当てられたメモリ量(len より大きくなる場合がある)であり、shrink_to_fit() は余分な領域を解放する。.get() 安全なアクセスを保証します。範囲外のアクセスが発生した場合、パニックを引き起こす代わりに None を返します。


(3) ▶ サンプル:ベクトルの走査と反復処理の方法 (難易度 ⭐⭐)

RUST
// ============================================
// Vec: iteration, mutation, and functional methods
// ============================================

fn main() {
    let mut prices = vec![29.9, 49.9, 15.5, 99.0, 8.8];

    // Method 1: for-in by reference
    print!("Prices: ");
    for p in &prices {
        print!("{:.1} ", p);
    }
    println!();

    // Method 2: mutable iteration (add tax)
    for p in &mut prices {
        *p *= 1.1;  // 10% tax
    }
    println!("With 10% tax: {:?}", prices);

    // Method 3: .iter().enumerate()
    for (i, p) in prices.iter().enumerate() {
        println!("Item {}: {:.2}", i, p);
    }

    // Method 4: functional style -- map, filter, sum
    let total: f64 = prices.iter().sum();
    println!("Total: {:.2}", total);

    let above_30: Vec<f64> = prices.iter()
        .filter(|&&p| p > 30.0)
        .copied()
        .collect();
    println!("Items above 30: {:?}", above_30);

    let discounted: Vec<f64> = prices.iter()
        .map(|p| p * 0.8)  // 20% off
        .collect();
    println!("20% discount: {:?}", discounted);
}

出力:

TEXT
Prices: 29.9 49.9 15.5 99.0 8.8
With 10% tax: [32.89, 54.89, 17.05, 108.9, 9.68]
Item 0: 32.89
Item 1: 54.89
Item 2: 17.05
Item 3: 108.90
Item 4: 9.68
Total: 223.41
Items above 30: [32.89, 54.89, 108.9]
20% discount: [26.312, 43.912, 13.640000000000002, 87.12, 7.744]

Vec は複数の反復処理メソッドをサポートしています。読み取り専用の反復処理には &v、要素の変更には &mut v を使用します。関数型スタイルのメソッドである .iter().map().filter()、および .sum() により、データ処理を簡潔かつ洗練されたものにすることができます。.collect() は、イテレータを再び Vec に変換します。


(4) ▶ サンプル:Vec と配列の相互変換 (難易度 ⭐⭐)

RUST
// ============================================
// Conversion between Vec and arrays
// ============================================

fn main() {
    // Array -> Vec: via .to_vec()
    let arr: [i32; 5] = [10, 20, 30, 40, 50];
    let vec_from_arr: Vec<i32> = arr.to_vec();
    println!("Array to Vec: {:?}", vec_from_arr);

    // Array -> Vec: via .iter().copied().collect()
    let vec_via_iter: Vec<i32> = arr.iter().copied().collect();
    println!("Via iter: {:?}", vec_via_iter);

    // Vec -> Array: via try_into() (returns Result)
    let vec_data = vec![1, 2, 3, 4];
    // let bad_array: [i32; 5] = vec_data.try_into().unwrap();  // PANIC: length mismatch
    let ok_array: [i32; 4] = vec_data.try_into().unwrap();
    println!("Vec to array: {:?}", ok_array);

    // Vec -> slice (zero-cost, no copy)
    let slice: &[i32] = &ok_array[1..3];
    println!("Slice of array: {:?}", slice);

    // Vec from iterator
    let squares: Vec<i32> = (1..=5).map(|x| x * x).collect();
    println!("Squares: {:?}", squares);

    // Vec from repeated value
    let repeated = vec!["hello"; 3];
    println!("Repeated: {:?}", repeated);
}

出力:

TEXT
Array to Vec: [10, 20, 30, 40, 50]
Via iter: [10, 20, 30, 40, 50]
Vec to array: [1, 2, 3, 4]
Slice of array: [20, 30]
Squares: [1, 4, 9, 16, 25]
Repeated: ["hello", "hello", "hello"]

.to_vec() を使用すると、配列を Vec に変換できます。.try_into().unwrap() を使用すると、Vec を配列に変換できます。この際、長さが一致していないとパニックが発生します。Vec は &[T] を使用することで、コストなしでスライスに変換できます。.collect() を使用してイテレータから Vec を作成するのは、よく使われる手法です。


(5) ▶ サンプル:題 5:総合演習—ショッピングカートと価格計算(難易度 ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example: Vec Insert, Delete, Update, Query + Functional Iteration
// ============================================

#[derive(Debug, Clone)]
struct Item {
    name: String,
    price: f64,
    quantity: u32,
}

impl Item {
    fn new(name: &str, price: f64, quantity: u32) -> Self {
        Item { name: name.to_string(), price, quantity }
    }
    fn subtotal(&self) -> f64 {
        self.price * self.quantity as f64
    }
}

fn print_cart(cart: &[Item]) {
    println!("{:<15} {:>8} {:>6} {:>10}", "Products", "Unit Price", "Quantity", "Subtotal");
    println!("{}", "-".repeat(42));
    for item in cart {
        println!("{:<15} {:>8.2} {:>6} {:>10.2}",
            item.name, item.price, item.quantity, item.subtotal());
    }
    let total: f64 = cart.iter().map(|i| i.subtotal()).sum();
    println!("{}", "-".repeat(42));
    println!("{:<15} {:>8} {:>6} {:>10.2}", "Total", "", "", total);
}

fn main() {
    let mut cart: Vec<Item> = Vec::new();
    cart.push(Item::new("Milk", 5.5, 2));
    cart.push(Item::new("Bread", 8.0, 1));
    cart.push(Item::new("Eggs", 12.5, 3));
    cart.push(Item::new("Cola", 3.0, 4));

    println!("=== Shopping Cart ===");
    print_cart(&cart);

    cart.retain(|i| i.name != "Cola");
    println!("\n--- Remove Cola after ---");
    print_cart(&cart);

    cart.push(Item::new("Butter", 15.0, 2));
    if let Some(eggs) = cart.iter_mut().find(|i| i.name == "Eggs") {
        eggs.quantity += 2;
    }
    println!("\n--- Add Butter + Add Eggs Quantity ---");
    print_cart(&cart);

    let total: f64 = cart.iter().map(|i| i.subtotal()).sum();
    let tax = total * 0.08;
    println!("\nBefore taxes: {:.2}, Tax(8%): {:.2}, Total due: {:.2}", total, tax, total + tax);

    let expensive: Vec<&Item> = cart.iter().filter(|i| i.price > 10.0).collect();
    println!("Unit Price > 10 Items priced at yuan: {:?}", expensive.iter().map(|i| &i.name).collect::<Vec<_>>());
}

出力:

TEXT
=== Shopping Cart ===
Products               Unit Price   Quantity       Subtotal
------------------------------------------
Milk              5.50      2      11.00
Bread             8.00      1       8.00
Eggs             12.50      3      37.50
Cola              3.00      4      12.00
------------------------------------------
Total                                68.50

--- Remove Cola after ---
Products               Unit Price   Quantity       Subtotal
------------------------------------------
Milk              5.50      2      11.00
Bread             8.00      1       8.00
Eggs             12.50      3      37.50
------------------------------------------
Total                                56.50

--- Add Butter + Add Eggs Quantity ---
Products               Unit Price   Quantity       Subtotal
------------------------------------------
Milk              5.50      2      11.00
Bread             8.00      1       8.00
Eggs             12.50      5      62.50
Butter           15.00      2      30.00
------------------------------------------
Total                               103.50

Before taxes: 103.50, Tax(8%): 8.28, Total due: 111.78
Unit Price > 10 Items priced at yuan: ["Eggs", "Butter"]

この例では、pushretain(条件付き削除)、iter_mut().find()(条件付き変更)、およびmap/filter/sum(機能統計)といった、Vecの主要な操作を組み合わせて使用しています。retainは、removeよりも一括削除に適しています。


❓ よくある質問

Q Vecのサイズ変更時には何が起こりますか?パフォーマンスは良好ですか?
A Vecがいっぱいになると、2倍の大きさの新しいメモリブロックを割り当て、古いデータをそこにコピーし、古いメモリを解放します。
Q Vecと配列のどちらを選ぶべきですか?
A 要素数がコンパイル時に正確にわかっており、その数が少ない場合は配列を使用してください。要素数が動的に変化する場合や不明な場合は、Vecを使用してください。
Q vec!["a", "b"]vec!["a"; 2] の違いは何ですか?
A 前者は要素のリスト(各要素は個別に計算されます)であり、後者は同じ値の複製です。
Q .iter() と into_iter() の違いは何ですか?
A .iter() は参照を返す(所有権を移転しない)のに対し、into_iter() は Vec を消費し、所有権を持つイテレータを返します。
Q Vec データはヒープ上に格納されていますが、スタック上の Vec 変数には何が格納されているのでしょうか?
A スタックには 3 つの usize 値、すなわちヒープデータへのポインタ、長さ (len)、および容量 (capacity) が格納されており、合計で 24 バイト(64 ビットシステムの場合)になります。

📖 まとめ


📝 練習問題

  1. 難易度 ⭐: 1 から 10 までの数値を含む Vec<i32> を作成し、push を使って 11 と 12 を追加し、次に pop を使って最後の 3 つの要素を削除し、最後に Vec を出力してください。
  2. 難易度 ⭐⭐: Vec からすべての偶数を削除し、奇数のみを残す関数 fn remove_evens(v: &mut Vec<i32>) を記述してください。mainvec![1, 2, 3, 4, 5, 6, 7, 8] を使用してテストしてください。結果は [1, 3, 5, 7] になるはずです。
  3. 難易度 ⭐⭐⭐:ショッピングカートプログラムをシミュレートしてください。struct Item { name: String, price: f64, quantity: u32 } を定義してください。Vec<Item> としてショッピングカートを作成し、商品の追加、商品名による商品の削除、数量の変更、およびショッピングカートの合計金額の出力を実装してください。main で、その一連の処理を実演してください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%