在rust中,如何实现在运行时对全局变量设置和读取,这个问题困扰了我一段时间。因为在rust中,全局变量是不能在运行时修改的。rust的全局变量是属于全局静态变量,使用关键词static
来定义,如下:
static mut count: usize = 0;
pub fn init_config(file_path: &str) {
count = count + 1;
}
编译器会告诉你修改count
不安全(unsafe)。
➜ las git:(feature/self-update-with-watcher-fork) ✗ cargo build
Compiling las v0.1.1 (/root/code/las)
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
--> src/config/cfg.rs:35:13
|
35 | count = count + 1;
| ^^^^^ use of mutable static
|
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
--> src/config/cfg.rs:35:5
|
35 | count = count + 1;
| ^^^^^^^^^^^^^^^^^ use of mutable static
|
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
For more information about this error, try `rustc --explain E0133`.
error: could not compile `las` (bin "las") due to 2 previous errors
一定要修改的话,需要加unsafe
块包裹住:
static mut count: usize = 0;
pub fn init_config(file_path: &str) {
unsafe { count = count + 1 };
}
但是,在rust中使用unsafe
是一件不好的事情,我们应该尽量避免使用unsafe
,这里有另一个方案可以实现在运行时设置全局变量,就是使用once_cell
这个包,虽然它的底层也是基于unsafe
来实现的,但是它对“不安全”代码进行了一定的包装。具体用法如下:
static CFG: OnceCell<Conf> = OnceCell::new();
// 初始化和设置
pub fn init_config(file_path: &str) {
CFG.get_or_init(|| load_config(file_path));
}
// 读取
pub fn get_config() -> &'static Conf {
CFG.get().unwrap()
}
可以看到,我们可以在运行时使用once_cell
的get_or_init
方法对全局变量进行初始化设置,它还有set
方法也可以在运行时对全局变量进行设置,get
方法在运行时对全局变量进行读取。从而实现不直接使用unsafe
块,来操作全局变量。而且once_cell
使用的全局变量完全不必申明为可变(mut
)变量。
我们可以通过追踪once_cell::get_or_init
方法,可以看到在initialize
方法中unsafe
块中,将value
设置给了*slot
。
可以看到,once_cell
对全局变量的操作进行了安全封装,因此,建议使用once_cell
进行全局变量的操作,而不是到处使用unsafe
块进行不安全的操作。
^注意:实践证明,OnceCell
只能调用set()
对全局变量设置一次,后续再调用set()
就会报错。要反复读写甚至多线程中读写全局变量,应该使用RwLock
,详情参考新的文章《初学rust,多线程中读写全局变量》