Rust固定容量向量库fixed-capacity-vec的使用:高效内存管理下的定长数组解决方案

Rust固定容量向量库fixed-capacity-vec的使用:高效内存管理下的定长数组解决方案

FixedCapacityVec是一个简单的特殊用途数据结构,主要用于需要逐步构建固定大小表格的应用场景。

与标准库类型的比较

以下所有类型都只在堆上存储实际缓冲区,并且可以在不复制数据的情况下相互转换:

类型 大小和表示形式(如在栈上) 是否总是满的 可变性
Vec 3个字:指针、长度、容量 可能 可无限追加
Box<[T]> 2个字:指针、长度=容量 总是 运行时固定长度
FixedCapacityVec<T, N> 2个字:指针、长度 可能 可追加,但容量在编译时固定
Box<[T; N]> 1个字:指针 总是 编译时固定长度

内存布局示意图:

         Vec<T>              HEAP                     Box<[T]>          HEAP 
             +----------+     +------------+            +----------+     +----------+
             | pointer ----> | T          |            | pointer ----> | T        |
             | length   |     | T ...      |            | length   |     | T ...    |
             | capacity |     +--length----+            +----------+     +--length--+
             +----------+     | (spare)    |
                              +--capacity--+

   FixedCapacityVec<T, N>    HEAP                     Box<[T; N]>        HEAP
             +----------+     +-------------+            +----------+     +---------+
             | pointer ----> | T           |            | pointer ----> | T       |
             | length   |     | T ...       |            +----------+     | T ...   |
             +----------+     |--length-----|                             +--N------+
                              | (spare)     |
                              +--N----------|

与其他库的vec-like类型的比较

FixedCapacityVecArrayVecSmallVec有不同的用途。

FixedCapacityVec的数据总是存储在堆上。

相比VecFixedCapacityVec的优势在于:

  • 自身尺寸更小
  • 不需要表示或查找容量
  • 可以非常廉价地转换为装箱数组、Vec或装箱切片,无需复制

ArrayVec也是一个固定容量的vec-like结构,但它的数据与元数据存储在一起。要将一个满的ArrayVec转换为装箱数组,必须复制数据。

SmallVec的优势在于可以在栈上存储少量数据,但SmallVec本身并不特别小。

完整示例代码

use fixed_capacity_vec::FixedCapacityVec;

fn main() {
    // 创建一个容量为5的FixedCapacityVec
    let mut fcv: FixedCapacityVec<i32, 5> = FixedCapacityVec::new();
    
    // 添加元素
    fcv.push(1);
    fvc.push(2);
    fvc.push(3);
    
    println!("Length: {}, Capacity: {}", fcv.len(), fcv.capacity()); // 长度:3, 容量:5
    
    // 转换为Box<[i32; 5]>
    let boxed_array: Box<[i32; 5]> = fcv.into_boxed_array().unwrap();
    
    // 转换为Vec
    let vec: Vec<i32> = boxed_array.into_vec();
    
    println!("Vec: {:?}", vec);
}

这个示例展示了如何:

  1. 创建一个固定容量的向量
  2. 向其中添加元素
  3. 将其转换为装箱数组
  4. 再转换为普通Vec

所有转换都不需要复制实际数据。

扩展完整示例代码

use fixed_capacity_vec::FixedCapacityVec;

fn main() {
    // 1. 创建固定容量为10的向量
    let mut fcv: FixedCapacityVec<String, 10> = FixedCapacityVec::new();
    
    // 2. 添加元素
    for i in 0..7 {
        fcv.push(format!("Item-{}", i));
    }
    
    // 3. 检查长度和容量
    println!("当前长度: {}, 固定容量: {}", fcv.len(), fcv.capacity());
    
    // 4. 访问元素
    println!("第一个元素: {}", fcv[0]);
    println!("最后一个元素: {}", fcv.last().unwrap());
    
    // 5. 转换为Box<[String; 10]>
    match fcv.into_boxed_array() {
        Ok(boxed_array) => {
            println!("转换为Box<[String; 10]>成功");
            
            // 6. 转换为Vec
            let vec: Vec<String> = boxed_array.into_vec();
            println!("Vec内容: {:?}", vec);
        }
        Err(fcv) => {
            println!("转换失败,当前长度: {}", fcv.len());
        }
    }
    
    // 7. 错误处理示例
    let mut small_fcv: FixedCapacityVec<i32, 3> = FixedCapacityVec::new();
    small_fcv.push(1);
    small_fcv.push(2);
    
    // 尝试转换为数组切片
    if let Ok(slice) = small_fcv.as_slice() {
        println!("切片内容: {:?}", slice);
    }
    
    // 尝试转换为可变切片
    if let Ok(mut_slice) = small_fcv.as_mut_slice() {
        mut_slice[1] = 99;
        println!("修改后的可变切片: {:?}", mut_slice);
    }
}

这个扩展示例展示了:

  1. 创建带有String类型的FixedCapacityVec
  2. 批量添加元素
  3. 访问元素的各种方式
  4. 转换时的错误处理
  5. 切片操作的使用
  6. 更完整的类型转换流程

1 回复

Rust固定容量向量库fixed-capacity-vec的使用:高效内存管理下的定长数组解决方案

介绍

fixed-capacity-vec是一个Rust库,提供了固定容量的向量实现。与标准库中的Vec不同,fixed-capacity-vec在创建时就分配了固定大小的内存,不会动态扩容,这使得它在需要确定内存使用量的场景下非常有用。

主要特点:

  • 预先分配固定容量,运行时不会重新分配内存
  • Vec类似的API接口
  • 更可预测的内存使用模式
  • 适合嵌入式系统或性能敏感场景

安装

在Cargo.toml中添加依赖:

[dependencies]
fixed-capacity-vec = "0.1"

基本使用方法

创建固定容量向量

use fixed_capacity_vec::FixedCapacityVec;

// 创建一个容量为10的固定向量
let mut vec = FixedCapacityVec::<i32, 10>::new();

添加元素

vec.push(1);
vec.push(2);
vec.push(3);

assert_eq!(vec.len(), 3);

访问元素

// 通过索引访问
let first = vec[0];  // 1

// 使用get方法,返回Option
let fourth = vec.get(3);  // None

迭代

for item in &vec {
    println!("{}", item);
}

容量检查

assert_eq!(vec.capacity(), 10);  // 固定容量
assert!(!vec.is_full());         // 是否已满

// 填充剩余空间
while !vec.is_full() {
    vec.push(42);
}

assert!(vec.is_full());

高级用法

从数组初始化

let mut vec = FixedCapacityVec::from_array([1, 2, 3, 4, 5]);

转换为数组

let array: [i32; 5] = vec.into_array().unwrap();

使用try_push处理已满情况

let mut vec = FixedCapacityVec::<i32, 3>::new();
vec.try_push(1).unwrap();
vec.try_push(2).unwrap();
vec.try_push(3).unwrap();

if let Err(e) = vec.try_push(4) {
    println!("Vector is full: {}", e);
}

性能考虑

fixed-capacity-vec在以下场景特别有用:

  • 避免动态分配的开销
  • 需要确保内存使用不会超过特定限制
  • 嵌入式系统等资源受限环境

与标准Vec的对比

use std::time::Instant;
use fixed_capacity_vec::FixedCapacityVec;

fn main() {
    const SIZE: usize = 100_000;
    
    // 测试标准Vec
    let start = Instant::now();
    let mut std_vec = Vec::with_capacity(SIZE);
    for i in 0..SIZE {
        std_vec.push(i);
    }
    println!("Std Vec took: {:?}", start.elapsed());
    
    // 测试FixedCapacityVec
    let start = Instant::now();
    let mut fixed_vec = FixedCapacityVec::<usize, SIZE>::new();
    for i in 0..SIZE {
        fixed_vec.push(i);
    }
    println!("Fixed Vec took: {:?}", start.elapsed());
}

在不需要动态扩容的场景下,fixed-capacity-vec通常能提供更稳定的性能表现。

完整示例代码

use fixed_capacity_vec::FixedCapacityVec;
use std::time::Instant;

fn main() {
    // 示例1: 基本使用
    let mut vec = FixedCapacityVec::<i32, 5>::new();
    
    // 添加元素
    vec.push(10);
    vec.push(20);
    vec.push(30);
    
    // 访问元素
    println!("第一个元素: {}", vec[0]);  // 10
    println!("第三个元素: {:?}", vec.get(2));  // Some(30)
    println!("第六个元素: {:?}", vec.get(5));  // None
    
    // 迭代
    println!("所有元素:");
    for num in &vec {
        println!("{}", num);
    }
    
    // 示例2: 高级用法
    let mut vec2 = FixedCapacityVec::from_array([1, 2, 3]);
    println!("从数组创建后的长度: {}", vec2.len());  // 3
    
    // 尝试添加元素
    match vec2.try_push(4) {
        Ok(_) => println!("添加成功"),
        Err(e) => println!("添加失败: {}", e),  // 根据容量不同结果不同
    }
    
    // 转换为数组
    if let Ok(arr) = vec2.into_array() {
        println!("转换后的数组: {:?}", arr);
    }
    
    // 示例3: 性能比较
    const SIZE: usize = 100_000;
    
    let start = Instant::now();
    let mut std_vec = Vec::with_capacity(SIZE);
    for i in 0..SIZE {
        std_vec.push(i);
    }
    println!("标准Vec耗时: {:?}", start.elapsed());
    
    let start = Instant::now();
    let mut fixed_vec = FixedCapacityVec::<usize, SIZE>::new();
    for i in 0..SIZE {
        fixed_vec.push(i);
    }
    println!("FixedCapacityVec耗时: {:?}", start.elapsed());
    
    // 示例4: 嵌入式系统场景
    let mut sensor_data = FixedCapacityVec::<f32, 100>::new();
    for _ in 0..100 {
        sensor_data.push(0.0);  // 初始化传感器数据缓冲区
    }
    println!("传感器缓冲区大小: {}", sensor_data.capacity());  // 100
}

这个完整示例演示了:

  1. 基本使用方法(创建、添加、访问、迭代)
  2. 高级用法(从数组创建、try_push、转换为数组)
  3. 性能比较测试
  4. 嵌入式系统应用场景

所有代码都包含注释说明每个部分的用途和预期输出。

回到顶部