Rust 1.70新增了两个类型:OnceCell和OnceLock,它们都用于一次性初始化共享数据。其中OnceLock是OnceCell的线程安全版本。
只能写入一次的cell,它无需复制或替换就能获得对其内部值的共享&T引用(不像Cell),也没有运行时借用检查(不像RefCell)。但是,只能获得不可变引用,除非是cell本身的可变引用。
use std::cell::OnceCell;
fn main() {
let cell = OnceCell::new();
assert!(cell.get().is_none());
let value: &String = cell.get_or_init(|| {
"Hello, World!".to_string()
});
println!("{}", value);
// 修改cell内的值
let value1 = cell.set("value".to_string());
println!("{:?}", value1);
println!("{:?}", cell.get());
}
执行结果:
Hello, World!
Err("value")
Some("Hello, World!")
在这个例子中,我们在给cell赋值“Hello, World”后,再次修改cell的值为“value”。执行结果会打印出错误,cell的值没有发生改变。
在这里的get_or_init函数,可以获取OnceCell的内容,如果为空,则使用闭包对其进行初始化。如果闭包发生恐慌,则将恐慌传播给调用方,并且cell保持未初始化状态。
use std::cell::OnceCell;
fn main() {
let cell = OnceCell::new();
// let value = cell.get_or_init(|| 92);
// assert_eq!(value, &92);
let value = cell.get_or_init(|| unreachable!());
assert_eq!(value, &92);
}
上面的代码编译不通过,因为value的值是unknow的。
只能写入一次的同步原语,这种类型是线程安全的OnceCell,可以在静态中使用。
use std::sync::OnceLock;
static WINNER: OnceLock<&str> = OnceLock::new();
fn main() {
let winner = std::thread::scope(|s| {
s.spawn(|| WINNER.set("thread"));
std::thread::yield_now(); // give them a chance...
WINNER.get_or_init(|| "main")
});
println!("{winner} wins!");
}
执行结果有可能是:"main wins!",也有可能是"thread wins!"。
lazy_static和once_cell等crate在过去已经满足了这一需求,但现在这些功能成为了标准库的一部分,是从once_cell的unsync和sync模块移植过来的。将来可能还会有更多的方法被稳定下来,例如LazyCell和LazyLock类型,它们是延迟初始化函数。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8