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。