static mut的替代

发布时间:2024-01-02 08:26
最后更新:2024-01-02 11:24
所属分类:
Rust

在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中的限制,对需要在全局共享的内容使用同步原语包装,使其能够满足在多线程情况下的所有使用要求。为了达到这个目的,就需要使用标准库中提供的MutexMutexGuardonce_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();
}

索引标签
Rust
static mut
once cell
mutex