Skip to content

Vector 向量

Vec<T> 是 Rust 标准库中提供的动态数组,本质是一个 struct,长度可在运行时动态增减,弥补了数组长度固定的限制。

  • Vec 只能储存相同类型的值(可借助枚举间接储存多种类型)
  • 数据存放在上,栈上保存一个超胖指针(ptr + len + cap)
  • 整体所有权: 离开作用域时,Vec 及其所有元素都会被释放
rust
pub struct Vec<T> {
    ptr: Unique<T>, // 指向堆内存的原始指针 (8字节)
    cap: usize,     // 容量(当前最多可存放多少元素)(8字节)
    len: usize,     // 当前元素数量 (8字节)
}

新建 vector

语法说明
Vec::new()创建一个空的 Vec,需要显式类型标注
Vec::from()从数组或切片创建 Vec,可自动推导类型
vec![]创建 Vec,可自动推导类型,支持重复元素语法 [value; count]
Vec::with_capacity()预分配容量,适合已知元素数量时使用,避免频繁扩容带来的性能损失
rust
// Vec::new 函数 创建空 Vec(需显式类型标注)
let v: Vec<i32> = Vec::new();

// Vec::from 函数
let v: Vec<i32> = Vec::from([1, 2, 3]);

// vec! 宏(自动推导类型)
let v = vec![1, 2, 3];

// vec![value; count]: 创建 count 个相同初始值的 Vec
let v = vec![0; 5];       // [0, 0, 0, 0, 0]
let v = vec!["hi"; 3];    // ["hi", "hi", "hi"]

// Vec::with_capacity: 预分配容量,避免频繁扩容
let mut v: Vec<i32> = Vec::with_capacity(10);

Vec::new()Vec::with_capacity() 需要显式类型标注;

Vec::fromvec! 宏可自动推导类型。

vec 也可以和数组直接比较: assert_eq!(vec![1,2,3], [1,2,3])

增加元素

语法: push(value)

push() 方法添加元素到末尾

rust
let mut v = vec![1, 2, 3, 4, 5]; // 必须声明 mut
v.push(6);

push() 方法会获得 vector 的可变引用,vector 必须声明为 mut

读取元素

语法: []get(index)

  • 使用 [] 直接索引读取元素
  • 使用 get() 方法读取元素

区别: [] 越界会 panic;get() 越界返回 None

rust
let v = vec![1, 2, 3, 4, 5];

// 使用索引 []
let third: &i32 = &v[2];
println!("The third element is {third}");

// 使用 get() 方法,返回 Option<&T>
let third: Option<&i32> = v.get(2);
match third {
    Some(third) => println!("The third element is {third}"),
    None => println!("There is no third element."),
}

get() 不会获得所有权,只返回不可变引用

遍历 vector 中的元素

  • 使用 for 循环遍历不可变引用
rust
let v = vec![100, 32, 57];

// 遍历不可变引用
for i in &v {
    println!("{i}");
}
  • 使用 for 循环遍历可变引用修改元素
rust
let mut v = vec![100, 32, 57];

// 遍历可变引用,修改元素
for i in &mut v {
    *i += 50;
}
  • 使用 for 循环直接获得所有权,消耗 vector
rust
let v = vec![100, 32, 57];

// 使用迭代器方法(获得所有权,v 被移动)
for i in v {
    println!("{i}");
}

遍历时使用 &v 借用不可变引用;使用 &mut v 借用可变引用;直接使用 v 会获得所有权

使用枚举来储存多种类型

  • vector 只能储存相同类型的值
  • 枚举本身就是一个类型,可以在 vector 中储存多种类型的数据
rust
enum SpreadsheetCell {
    Int(i32),
    Float(f64),
    Text(String),
}

let row = vec![
    SpreadsheetCell::Int(3),
    SpreadsheetCell::Text(String::from("blue")),
    SpreadsheetCell::Float(10.12),
];

// 遍历枚举 vector
for cell in &row {
    match cell {
        SpreadsheetCell::Int(i) => println!("Int: {i}"),
        SpreadsheetCell::Float(f) => println!("Float: {f}"),
        SpreadsheetCell::Text(s) => println!("Text: {s}"),
    }
}

vector 本身类型统一为 Vec<SpreadsheetCell>;访问具体值时需用 matchif let 模式匹配

数组与向量的区别

特性arrayVec(向量)
长度固定动态
内存分配
语法[T; n]Vec<T>
索引usize 类型usize 类型
越界行为panicpanic

内存布局与扩容

Vec 在栈上保存一个超胖指针(24 字节),包含三段元数据(各占一个机器字长):

字段含义
ptr指向堆上数据的起始地址
len当前元素数量
cap当前容量(最多可存放多少元素,不够时触发扩容)

扩容机制: 当插入新元素时容量不足,Vec 会申请一块约为原来两倍大小的新堆内存,将原有元素全部拷贝过去,并更新超胖指针的元数据。

Rust 官方文档指出扩容策略并未固定,未来可能调整,但目前实现通常是 2 倍扩容。

rust
let mut v = vec![11, 22, 33];
println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=3

v.push(44);  // 容量不足,触发扩容(重新分配内存 + 拷贝)
println!("len={}, cap={}", v.len(), v.capacity()); // len=4, cap=6

频繁扩容或大量元素拷贝会降低性能,有两种方式减少扩容:

  • Vec::with_capacity(n): 创建时预先分配 n 个元素的容量
  • reserve(n): 对已有的 Vec 追加至少 n 个空闲容量
rust
let mut v = Vec::with_capacity(3);
v.push(11); v.push(22); v.push(33);
println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=3

v.reserve(10);  // 追加至少 10 个空闲位置
println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=13

v.reserve(5);   // 已有 10 个空闲,足够,什么也不做
println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=13

常用方法

查询方法

rust
let v = vec![1, 2, 3, 4, 5];

v.len();              // 返回元素个数 → usize
v.is_empty();         // 返回是否为空 → bool
v.first();            // 返回首个元素 → Option<&T>(借用不可变引用)
v.last();             // 返回最后一个元素 → Option<&T>(借用不可变引用)
v.contains(&3);       // 返回是否包含某值 → bool(借用不可变引用)
v.iter().position(|x| *x == 3); // 返回首个满足条件的索引 → Option<usize>(借用不可变引用)

增删方法

rust
let mut v = vec![3, 1, 4, 1, 5, 9];

v.pop();                // 移除并返回最后一个元素 → Option<T>(获得所有权)
v.remove(0);            // 移除索引 0 的元素(后续元素左移),返回该元素(获得所有权)
v.insert(1, 10);        // 在索引 1 处插入 10(原有元素右移)
v.split_off(2);          // 从索引 2 处一分为二,返回后半部分的新 Vec(获得所有权)
v.drain(0..2);          // 移除索引 0..2 的元素,返回被移除元素的迭代器(获得所有权)
v.retain(|x| *x > 2);  // 只保留满足条件的元素(原地过滤)
v.dedup();              // 去除相邻重复元素(配合 sort 可实现完全去重)
v.truncate(3);          // 截断到长度 3,多余元素被删除;若目标长度 ≥ 当前长度则不做任何事
v.clear();              // 清空向量

排序

rust
let mut v = vec![3, 1, 4, 1, 5];
v.sort();                         // 对实现了 Ord 的类型排序(整数、字符串等)
v.sort_by(|a, b| b.cmp(a));       // 自定义排序(这里是降序)
v.sort_by_key(|k| std::cmp::Reverse(*k)); // 按 key 排序

let mut floats = vec![3.1f64, 1.4, 2.7];
floats.sort_by(|a, b| a.partial_cmp(b).unwrap()); // f64 没实现 Ord,需用 partial_cmp

合并与扩展

rust
let mut a = vec![1, 2, 3];
let b = vec![4, 5, 6];
a.extend(b.iter());      // 追加 b 的元素到 a(借用 b 的不可变引用)
a.extend([7, 8, 9]);     // 追加数组
a.append(&mut b);        // 将 b 的所有元素移动到 a,b 变为空向量(获得 b 的可变引用)

容量与预分配

rust
// Vec::with_capacity: 预先分配空间,适合已知元素数量时
let mut v: Vec<i32> = Vec::with_capacity(100);
v.push(1);
println!("len={}, cap={}", v.len(), v.capacity()); // len=1, cap=100

v.reserve(50);         // 追加至少 50 个空闲容量(已有 99 个空闲,什么也不做)
v.shrink_to_fit();     // 释放多余容量,仅保留当前元素所需空间

基于 MIT 协议发布