我是一个初学者,正在将一个熟悉的Cpp项目翻译成Rust。该项目包含一个名为Globals的类,该类存储全局配置参数。以下是其cpp文件的摘录:
static Globals &__get()
{
static Globals globals;
return globals;
}
const Globals &Globals::get()
{
auto &globals = __get();
if (!globals.initialized) {
throw std::runtime_error("Initialize globals first");
}
return globals;
}
void Globals::set(const Globals &globals)
{
__get() = globals;
}
我该如何将其翻译成Rust?据我所知,__get((实现了某种单例逻辑。我读过关于lazy_static板条箱的文章,以实现类似的目的,但每次我想读取变量值时解锁它似乎太冗长了。难道不可能在cpp代码中使用类似于Globals::get((的接口来实现这一点吗。
我很少发帖,所以如果我忘了什么,告诉我,我会提供详细信息。谢谢
我读过关于lazy_static机箱的文章,以实现类似的功能,但每次我想读取变量值时解锁变量似乎太冗长了。
理由很充分:;安全锈蚀";包括所谓的线程安全设计。一个未受保护的可变全局是非常不安全的。
哪个。。。这就是为什么与可变静态交互需要unsafe
(无论是读还是写(。
C++的翻译非常简单[0],可以很容易地从关于静态的参考部分推断出来,唯一真正的分歧是必须初始化Rust静态。
还要注意,如果你不需要突变,那么你可以lazy_static!只读值(您根本不需要互斥(,并且不需要解锁任何东西。
[0],尽管通过取消不必要的__get
而大大简化了
Rust要求安全代码中的内存安全,因此,安全代码中不能有可变的静态。你可以有一个原子静态(例如,请参阅AtomicBool或AtomicU64(,但对于普通类型,你需要某种锁定机制,如RwLock或Mutex(如果你喜欢性能,parking_lot机箱提供比Rust标准库更高性能的实现(
如果您不想自己处理锁定,我可以建议您使用getter/setter方法制作一个包装器对象吗?
use std::sync::{Arc, RwLock};
use once_cell::sync::Lazy;
static GLOBAL: Lazy<Global> = Lazy::new(Global::new);
struct GlobalThingymajig {
pub number: u32,
pub words: String,
}
pub struct Global(Arc<RwLock<GlobalThingymajig>>);
impl Global {
pub fn new() -> Self {
Self(Arc::new(RwLock::new(
GlobalThingymajig {
number: 42,
words: "The Answer to Life, The Universe, and Everything".into()
}
)))
}
pub fn number(&self) -> u32 {
self.0.read().unwrap().number
}
pub fn words(&self) -> String {
self.0.read().unwrap().words.clone()
}
pub fn set_number(&self, new_number: u32) {
let mut writer = self.0.write().unwrap();
writer.number = new_number;
}
pub fn set_words(&self, new_words: String) {
let mut writer = self.0.write().unwrap();
writer.words = new_words;
}
}
你可以在这里的Rust Playground 上看到这个例子