6 个版本

0.2.4 2023年4月30日
0.2.3 2022年6月21日
0.2.2 2022年2月1日
0.2.1 2020年8月3日
0.1.1 2020年8月3日

并发类别中排名 #174


tada中使用

MIT 许可证

31KB
271 行(不含注释)

FreezeBox:可原子延迟初始化的解引用容器。

此crate包含两种类似的容器类型:FreezeBoxMaybeBox。这两种容器都可以使用共享引用进行延迟初始化,并且都允许调用者获取容器内部的值的引用。

let x = FreezeBox::<String>::default();
x.lazy_init(String::from("hello"));
assert_eq!(x.len(), 5);

这对于首先共享的数据结构,但该数据结构的一些成员稍后进行初始化的情况很有用。

let x = FreezeBox::<String>::default();
let shared_x = Arc::new(x);
shared_x.lazy_init(String::from("hello"));
assert_eq!(shared_x.len(), 5);

FreezeBoxMaybeBox有一些共同的行为:它们只能初始化一次;初始化是原子的;初始化后的值可能永远不会被移除,除非通过使用into_inner方法消耗容器。

FreezeBoxMaybeBox的主要区别在于FreezeBox实现了DerefFreezeBox旨在在内部值在尝试使用之前始终存在的情况下使用。在这种情况下,尝试读取未初始化的FreezeBox是一个错误,因此尝试读取将导致恐慌。

MaybeBox旨在在内部值可能缺失的情况下使用。MaybeBox没有实现Deref;相反,我们需要调用get,它返回Option<&T>

let x = MaybeBox::<String>::default();
if some_runtime_config {
    x.lazy_init(String::from("hello"));
}
if let Some(val) = x.get() {
    println!("{}", val);
}

示例

此示例创建了一个共享数据结构,然后稍后初始化一个成员变量。

use freezebox::FreezeBox;
use std::sync::Arc;

/// A data structure that we will initialize late.
#[derive(Default)]
struct Resources {
    name: FreezeBox<String>
}

// Create an instance of the `Resources` struct, which contains an
// uninitialized `name` field.
let resources = Arc::new(Resources::default());

// Clone the Arc to emulate sharing with other threads, contexts,
// or data structures.
let res2 = resources.clone();

// Here we emulate another thread accessing the shared data structure.
// NOTE: it's still our responsibility to ensure that the FreezeBox
// is initialized before anyone dereferences it.
//
let func = move || {
    // explicit deref
    assert_eq!(*res2.name, "Hello!");
    // implicit deref allows transparent access to inner methods
    assert_eq!(res2.name.len(), 6);
};

resources.name.lazy_init("Hello!".to_string());
func();

与其他方法的比较

  1. Option<T>

延迟初始化需要可变访问Option。这很好,除非父结构已经共享。

  1. Mutex<Option<T>>

这解决了初始化延迟的问题,但要求每个调用者都要锁定 Mutex 并解包 Option。添加的代码和运行时开销可能并不理想,尤其是如果我们只需要对内部 T 的共享引用。

  1. lazy_static

lazy_static! 声明了一个隐藏的 static 变量,因此它不适合用于延迟初始化的结构体成员。它还要求初始化代码放置在声明点,并在内部使用自旋锁。

  1. once_cell

once_cell 通常比 lazy_static 更适合用于新的 Rust 代码,并且在多个线程争相初始化内部值的情况下是一个不错的选择。

OnceCell 没有实现 Deref,需要显式调用 get()get_or_init()。这类似于 MaybeBox,但在 FreezeBox 设计的使用场景中更为冗长,在该场景中,读者期望值已经初始化。

OnceCell 不对内部值进行 Box,这使得原子初始化更加复杂,因此 once_cell::sync::OnceCellno_std 环境中不可用。

安全性和兼容性

FreezeBox 与 no_std 项目兼容(不需要功能标志)。它可以在任何具有内存分配器的环境中使用。

FreezeBox 在内部使用不安全代码。为了确保正确性,单元测试在 Miri 下通过,并且不安全代码简单易懂。

最低支持的 Rust 版本是 1.48。

无运行时依赖