static mut的替代
发布时间:2024-01-02 08:26
最后更新:2024-01-02 11:24
在Rust中使用static mut
来实现可变全局变量会在编译期收获一条编译错误。但是这种全局静态变量在其他很多语言中都是一种非常方便的用法,比如保存一个全局可用的可变数据库连接,或者持有一个由用户指定的文件句柄。这种使用方法在很多程序中都是非常常见的。
那么在Rust中不能直接使用这种语言结构,就必须寻找其他的方法来允许我们满足功能需要。
为什么不可用
要解决这个问题首先就必须要了解为什么Rust里不允许static mut
变量的存在。
static mut
首先是一个mut
变量,这个变量在Rust中必须要遵循一个规则:同一时刻只允许存在一个mut
引用。因为static
变量的生命期是'static
的,而且在程序运行期间不会出现所有权转移的情况,所以在使用static
变量的时候,一般都是通过引用来使用的。所以,“同一时刻只允许存在一个mut
引用”的规则就成为static mut
变量无法避开但又无法满足的规则了。
但是从另一方面说,static mut
变量存在于程序全局,又不能保证其不会发生跨线程的访问,从而进一步无法保证其能够满足“同一时刻只存在一个mut
引用”的要求。
所以在Rust里就不允许static mut
的存在了。
替代方案
static mut
的替代方案就是根据Rust中的限制,对需要在全局共享的内容使用同步原语包装,使其能够满足在多线程情况下的所有使用要求。为了达到这个目的,就需要使用标准库中提供的Mutex
和MutexGuard
和once_cell
库中提供的OnceCell
几个封装结构。
如果使用Rust 1.70以后的版本,那么可以使用标准库中提供的std::sync::OnceLock
结构体替代once_cell
库提供的ocne_cell::sync::OnceCell
结构。
在这个替代方案中,主要使用了Mutex
提供的线程安全的可变引用来更新其中持有的值,从而使其产生了与static mut
相同的操作特性。
以下是这个替代方案的使用示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import std::sync::{Mutex, MutexGuard};
import once_cell::sync::OnceCell;
#[derive(Debug)]
struct AppState {
ready: bool,
}
static APP_STATE: OnceCell<Mutex<AppState>> = OnceCell::new();
fn access_app_state() -> MutexGuard<'static, AppState> {
// 获得并返回Mutex对象的锁,MutexGuard代表的就是已经获取到的Mutex对象的锁。
APP_STATE.get().unwrap().lock().unwrap()
}
fn change_app_state() {
// 获取到的Mutex对象的锁MutexGuard引用,在这个函数执行结束的时候就丢弃了,
// 所以Mutex上的锁也就自动释放了。而且Mutex的锁特性,可以保证在同一时刻只会存在一个mut引用。
let mut state = access_app_state();
state = !state.ready;
}
fn main() {
APP_STATE.set(Mutex::new(AppState {
ready: false,
}))
.unwrap();
change_app_state();
}
|
自Rust 1.63版本以后,Mutex::new()
默认就是const
的,所以在这种情况下,once_cell::sync::OnceCell
结构体就不必要使用了。所以上面的示例也可以简化成以下的样子。
同样的,也不必使用标准库中提供的std::sync::OnceLock
结构体了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import std::sync::{Mutex, MutexGuard};
#[derive(Debug)]
struct AppState {
ready: bool,
}
static APP_STATE: Mutex<AppState> = Mutex::new(AppState {
ready: false,
});
fn access_app_state() -> MutexGuard<'static, AppState> {
APP_STATE.lock().unwrap()
}
fn change_app_state() {
let mut state = access_app_state();
state = !state.ready;
}
fn main() {
change_app_state();
}
|