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中使用
31KB
271 行(不含注释)
FreezeBox:可原子延迟初始化的解引用容器。
此crate包含两种类似的容器类型:FreezeBox和MaybeBox。这两种容器都可以使用共享引用进行延迟初始化,并且都允许调用者获取容器内部的值的引用。
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);
FreezeBox和MaybeBox有一些共同的行为:它们只能初始化一次;初始化是原子的;初始化后的值可能永远不会被移除,除非通过使用into_inner方法消耗容器。
FreezeBox和MaybeBox的主要区别在于FreezeBox实现了Deref。FreezeBox旨在在内部值在尝试使用之前始终存在的情况下使用。在这种情况下,尝试读取未初始化的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();
与其他方法的比较
Option<T>
延迟初始化需要可变访问Option。这很好,除非父结构已经共享。
Mutex<Option<T>>
这解决了初始化延迟的问题,但要求每个调用者都要锁定 Mutex 并解包 Option。添加的代码和运行时开销可能并不理想,尤其是如果我们只需要对内部 T 的共享引用。
lazy_static! 声明了一个隐藏的 static 变量,因此它不适合用于延迟初始化的结构体成员。它还要求初始化代码放置在声明点,并在内部使用自旋锁。
once_cell 通常比 lazy_static 更适合用于新的 Rust 代码,并且在多个线程争相初始化内部值的情况下是一个不错的选择。
OnceCell 没有实现 Deref,需要显式调用 get() 或 get_or_init()。这类似于 MaybeBox,但在 FreezeBox 设计的使用场景中更为冗长,在该场景中,读者期望值已经初始化。
OnceCell 不对内部值进行 Box,这使得原子初始化更加复杂,因此 once_cell::sync::OnceCell 在 no_std 环境中不可用。
安全性和兼容性
FreezeBox 与 no_std 项目兼容(不需要功能标志)。它可以在任何具有内存分配器的环境中使用。
FreezeBox 在内部使用不安全代码。为了确保正确性,单元测试在 Miri 下通过,并且不安全代码简单易懂。
最低支持的 Rust 版本是 1.48。