Rust fallible_collections的使用:可回退的集合操作库,提供内存不足等场景的优雅错误处理

Rust fallible_collections的使用:可回退的集合操作库,提供内存不足等场景的优雅错误处理

Fallible Collections.rs

在Rust集合上实现了API,当发生分配错误时优雅地返回Result。 这很大程度上受到了RFC 2116的启发。

有用于VecBoxBTreeHashMap的可回退接口的API, 以及一个为原始Rust特性实现的TryClone特性和一个可回退的格式化宏。 您可以使用try_clone_derive crate为您的类型派生TryClone

开始使用

fallible_collections可在crates.io上获得。 建议在那里查看最新发布的版本,以及最新文档构建的链接。

将以下依赖项添加到您的Cargo清单中:

[dependencies]
fallible_collections = "0.5"

# 或
fallible_collections = { version = "0.5", features = ["std"] }

…并查看文档以了解如何使用它。

示例

使用FallibleBox接口的示例。

use fallible_collections::FallibleBox;

fn main() {
    // 创建一个普通的Box,但在分配失败时返回错误
    let mut a = <Box<_> as FallibleBox<_>>::try_new(5).unwrap();
    let mut b = Box::new(5);

    assert_eq!(a, b);
    *a = 3;
    assert_eq!(*a, 3);
}

使用FallibleVec接口的示例。

use fallible_collections::FallibleVec;

fn main() {
    // 创建一个普通的Vec<Vec<u8>>,但在分配失败时返回错误
    let a: Vec<Vec<u8>> = try_vec![try_vec![42; 10].unwrap(); 100].unwrap();
    let b: Vec<Vec<u8>> = vec![vec![42; 10]; 100];
    assert_eq!(a, b);
    assert_eq!(a.try_clone().unwrap(), a);
    ...
}

完整示例代码

// 使用 fallible_collections 库的完整示例
use fallible_collections::{FallibleBox, FallibleVec, try_vec};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // FallibleBox 示例
    // 尝试创建一个 Box,分配失败时返回错误
    let mut fallible_box = <Box<i32> as FallibleBox<i32>>::try_new(42)?;
    println!("FallibleBox value: {}", *fallible_box);
    
    // 修改 Box 中的值
    *fallible_box = 100;
    println!("Modified FallibleBox value: {}", *fallible_box);
    
    // FallibleVec 示例
    // 使用宏创建可回退的 Vec
    let fallible_vec: Vec<Vec<u8>> = try_vec![
        try_vec![1, 2, 3]?,
        try_vec![4, 5, 6]?,
        try_vec![7, 8, 9]?
    ]?;
    
    println!("FallibleVec contents:");
    for (i, inner_vec) in fallible_vec.iter().enumerate() {
        println!("  Vec {}: {:?}", i, inner_vec);
    }
    
    // 尝试克隆 Vec
    let cloned_vec = fallible_vec.try_clone()?;
    assert_eq!(fallible_vec, cloned_vec);
    println!("Clone successful!");
    
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_fallible_box() {
        let box_result = <Box<i32> as FallibleBox<i32>>::try_new(123);
        assert!(box_result.is_ok());
        assert_eq!(*box_result.unwrap(), 123);
    }
    
    #[test]
    fn test_fallible_vec() {
        let vec_result: Result<Vec<Vec<u8>>, _> = try_vec![
            try_vec![1, 2, 3].unwrap(),
            try_vec![4, 5, 6].unwrap()
        ];
        
        assert!(vec_result.is_ok());
        let vec = vec_result.unwrap();
        assert_eq!(vec, vec![vec![1, 2, 3], vec![4, 5, 6]]);
    }
}

许可证

根据以下任一许可证授权:

  • Apache License, Version 2.0
  • MIT license

由您选择。

贡献

除非您明确声明,否则任何有意提交包含在作品中的贡献, 如Apache-2.0许可证中所定义,均应按照上述双重许可,不附加任何额外条款或条件。


1 回复

Rust fallible_collections 使用指南

介绍

fallible_collections 是一个 Rust 库,提供了可回退的集合操作,专门用于处理内存分配可能失败的情况(如内存不足)。该库为标准库中的集合类型提供了 fallible(可失败)版本,允许开发者在内存敏感的环境中优雅地处理错误。

主要特性

  • 提供 FallibleVecFallibleHashMap 等可失败集合类型
  • 支持内存分配错误的显式处理
  • 与标准库集合 API 保持高度一致性
  • 适用于嵌入式系统和高可靠性应用场景

安装方法

在 Cargo.toml 中添加依赖:

[dependencies]
fallible_collections = "0.4"

基本用法示例

use fallible_collections::{FallibleVec, TryCollect, TryReserveError};

fn main() -> Result<(), TryReserveError> {
    // 创建可失败向量
    let mut vec = FallibleVec::new();
    
    // 尝试推送元素(可能因内存不足而失败)
    vec.try_push(42)?;
    vec.try_push(100)?;
    
    // 尝试预留容量
    vec.try_reserve(10)?;
    
    // 使用 try_collect 从迭代器创建集合
    let numbers: FallibleVec<i32> = (0..5).map(|x| x * 2).try_collect()?;
    
    println!("向量内容: {:?}", numbers);
    Ok(())
}

错误处理示例

use fallible_collections::{FallibleVec, TryReserveError};

fn process_data() -> Result<(), TryReserveError> {
    let mut data = FallibleVec::new();
    
    // 模拟内存不足情况
    match data.try_reserve(usize::MAX) {
        Ok(_) => println!("内存分配成功"),
        Err(TryReserveError::CapacityOverflow) => {
            eprintln!("容量溢出错误");
            return Err(TryReserveError::CapacityOverflow);
        }
        Err(TryReserveError::AllocError { .. }) => {
            eprintln!("内存分配失败");
            return Err(TryReserveError::AllocError { layout: std::alloc::Layout::new::<u8>() });
        }
    }
    
    Ok(())
}

HashMap 使用示例

use fallible_collections::{FallibleHashMap, TryReserveError};
use std::collections::hash_map::RandomState;

fn main() -> Result<(), TryReserveError> {
    let mut map = FallibleHashMap::new();
    
    // 尝试插入键值对
    map.try_insert("key1", "value1")?;
    map.try_insert("key2", "value2")?;
    
    // 尝试预留空间
    map.try_reserve(100)?;
    
    println!("映射内容: {:?}", map);
    Ok(())
}

完整示例代码

use fallible_collections::{FallibleVec, FallibleHashMap, TryCollect, TryReserveError};
use std::collections::hash_map::RandomState;

// 演示 FallibleVec 的基本操作
fn demo_fallible_vec() -> Result<(), TryReserveError> {
    println!("=== FallibleVec 演示 ===");
    
    // 创建新的可失败向量
    let mut vec = FallibleVec::new();
    
    // 尝试添加元素
    vec.try_push(10)?;
    vec.try_push(20)?;
    vec.try_push(30)?;
    
    println!("添加元素后: {:?}", vec);
    
    // 尝试预留容量
    vec.try_reserve(100)?;
    println!("预留容量后,容量: {}", vec.capacity());
    
    // 使用 try_collect 从迭代器创建向量
    let collected: FallibleVec<i32> = (0..10).map(|x| x * 3).try_collect()?;
    println!("从迭代器收集: {:?}", collected);
    
    Ok(())
}

// 演示 FallibleHashMap 的基本操作
fn demo_fallible_hashmap() -> Result<(), TryReserveError> {
    println!("\n=== FallibleHashMap 演示 ===");
    
    let mut map = FallibleHashMap::new();
    
    // 尝试插入键值对
    map.try_insert("name", "Alice")?;
    map.try_insert("age", "30")?;
    map.try_insert("city", "Beijing")?;
    
    println!("HashMap 内容: {:?}", map);
    
    // 尝试预留更多空间
    map.try_reserve(50)?;
    println!("预留空间后,容量: {}", map.capacity());
    
    // 访问元素
    if let Some(name) = map.get("name") {
        println!("获取到的名字: {}", name);
    }
    
    Ok(())
}

// 演示错误处理
fn demo_error_handling() -> Result<(), TryReserveError> {
    println!("\n=== 错误处理演示 ===");
    
    let mut vec = FallibleVec::new();
    
    // 尝试分配极大内存(应该会失败)
    match vec.try_reserve(usize::MAX) {
        Ok(_) => println!("意外成功:分配了极大内存"),
        Err(TryReserveError::CapacityOverflow) => {
            println!("正确处理:容量溢出错误");
        }
        Err(TryReserveError::AllocError { .. }) => {
            println!("正确处理:内存分配错误");
        }
    }
    
    Ok(())
}

fn main() -> Result<(), TryReserveError> {
    // 演示各种功能
    demo_fallible_vec()?;
    demo_fallible_hashmap()?;
    demo_error_handling()?;
    
    println!("\n=== 所有演示完成 ===");
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_basic_operations() -> Result<(), TryReserveError> {
        let mut vec = FallibleVec::new();
        vec.try_push(1)?;
        vec.try_push(2)?;
        assert_eq!(vec.len(), 2);
        Ok(())
    }
}

最佳实践

  1. 在内存受限环境中使用 fallible_collections
  2. 始终处理可能的分配错误
  3. 使用 ? 操作符简化错误传播
  4. 结合 #[cfg(feature = "...")] 条件编译在不同环境中切换实现

注意事项

  • 该库会增加一定的运行时开销
  • 需要显式处理所有可能的内存分配错误
  • 建议仅在确实需要处理内存分配失败的场景中使用

通过使用 fallible_collections,开发者可以在保持代码健壮性的同时,优雅地处理内存分配失败的情况。

回到顶部