Rust: Rust 向量 Vec:动态数组的增删改查

最后更新:2026-08-26

Vec<T>(向量)是 Rust 标准库中最常用的动态数组——可以在运行时增长或收缩,所有数据在堆上分配。它是 Rust 的"ArrayList"或"Python list"。

如果说数组是"固定大小的储物柜",Vec 就是"可伸缩的仓库"——不够用时自动扩容,不需要你手动管理内存。


1. 你将学到


2. 一个超市收银员的故事

(1) 痛苦:购物车的大小不知道

Tom 在开发一个超市收银系统,顾客的购物车:

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) Vec 与数组对比

维度 数组 [T; N] 向量 Vec<T>
大小 编译时固定 运行时动态增长
分配位置 栈(通常)
扩容 不支持 自动 2 倍扩容
Api 丰富度 有限 极丰富
访问速度 极快(栈上连续) 极快(堆上连续)
适用场景 固定大小、栈上分配 未知数量、频繁增减
性能开销 push 可能触发 reallocation

(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 遍历方式对比

方式 语法 所有权 Vec 是否可用
借用遍历 for x in &v 只读引用 可用
可变遍历 for x in &mut v 可变引用 可用(已改)
消费遍历 for x in v 转移所有权 不可用
迭代器 v.iter() 只读引用 可用
枚举迭代 v.iter().enumerate() 只读引用+索引 可用

4. Vec 示例

▶ 示例 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](5 个零)。push 尾部追加,pop 尾部弹出(返回 Option<T>)。len() 获取当前元素数。


▶ 示例 2:insert/remove 与容量管理(难度 ⭐⭐)

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 而非 panic。


▶ 示例 3:Vec 遍历与迭代方法(难度 ⭐⭐)

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"]

数组转 Vec 用 .to_vec()。Vec 转数组用 .try_into().unwrap()——长度必须匹配否则 panic。Vec 可以零成本转换为切片 &[T]。从迭代器用 .collect() 创建 Vec 是常用技巧。


▶ 示例 5:综合练习——购物车与价格计算(难度 ⭐⭐⭐)

RUST
// ============================================
// 综合示例:Vec 增删改查 + 函数式迭代
// ============================================

#[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}", "商品", "单价", "数量", "小计");
    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);
}

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!("=== 购物车 ===");
    print_cart(&cart);

    cart.retain(|i| i.name != "Cola");
    println!("\n--- 移除 Cola 后 ---");
    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--- 添加 Butter + 增加 Eggs 数量 ---");
    print_cart(&cart);

    let total: f64 = cart.iter().map(|i| i.subtotal()).sum();
    let tax = total * 0.08;
    println!("\n税前: {:.2}, 税(8%): {:.2}, 应付: {:.2}", total, tax, total + tax);

    let expensive: Vec<&Item> = cart.iter().filter(|i| i.price > 10.0).collect();
    println!("单价 > 10 元的商品: {:?}", expensive.iter().map(|i| &i.name).collect::<Vec<_>>());
}

输出:

TEXT 📖 仅展示
=== 购物车 ===
商品               单价   数量       小计
------------------------------------------
Milk              5.50      2      11.00
Bread             8.00      1       8.00
Eggs             12.50      3      37.50
Cola              3.00      4      12.00
------------------------------------------
合计                                68.50

--- 移除 Cola 后 ---
商品               单价   数量       小计
------------------------------------------
Milk              5.50      2      11.00
Bread             8.00      1       8.00
Eggs             12.50      3      37.50
------------------------------------------
合计                                56.50

--- 添加 Butter + 增加 Eggs 数量 ---
商品               单价   数量       小计
------------------------------------------
Milk              5.50      2      11.00
Bread             8.00      1       8.00
Eggs             12.50      5      62.50
Butter           15.00      2      30.00
------------------------------------------
合计                               103.50

税前: 103.50, 税(8%): 8.28, 应付: 111.78
单价 > 10 元的商品: ["Eggs", "Butter"]

此示例综合运用了 pushretain(条件删除)、iter_mut().find()(条件修改)、map/filter/sum(函数式统计)等 Vec 核心操作。retainremove 更适合批量删除。


❓ 常见问题

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. 难度 ⭐:创建一个 Vec<i32> 包含数字 1 到 10,用 push 添加 11 和 12,然后用 pop 移除最后 3 个元素,最终打印 Vec。
  2. 难度 ⭐⭐:编写函数 fn remove_evens(v: &mut Vec<i32>) 移除 Vec 中所有偶数,保留奇数。在 main 中用 vec![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%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏