用Option<…>作为静态变量来存储单例对象的原始全局指针,用get_or_insert_with方法来初始化单例对象
最严谨的方法是用 Arc
use std::sync::Arc;
use std::sync::Mutex;
struct Point {
pub x: u32,
pub y: u32,
}
impl Point {
pub fn get_instance() -> Arc<Mutex<Point>> {
// 全局变量作为Option类型,get_or_insert_with在判断自己是否为None时,
// 也可能有多线程竞争的问题,最安全的方法是给Option也套一个锁。
// 可以使用第三方库once_cell实现更简单的写法
static mut POINT: Option<Arc<Mutex<Point>>> = None;
unsafe {
// Rust中使用可变静态变量都是unsafe的
POINT.get_or_insert_with(|| {
// 初始化单例对象的代码
Arc::new(Mutex::new(Point {x: 0, y: 0}))
}).clone()
}
}
}
once_cell提供了unsync::OnceCell和sync::OnceCell这两种Cell(字面意思,前者用于单线程,后者用于多线程),用来存储堆上的信息,并且具有最多只能赋值一次的特性
use std::{env, io};
use once_cell::sync::OnceCell;
#[derive(Debug)]
pub struct Logger {
// ...
}
static INSTANCE: OnceCell<Logger> = OnceCell::new();
impl Logger {
pub fn global() -> &'static Logger {
INSTANCE.get().expect("logger is not initialized")
}
fn from_cli(args: env::Args) -> Result<Logger, std::io::Error> {
// ...
}
}
fn main() {
let logger = Logger::from_cli(env::args()).unwrap();
INSTANCE.set(logger).unwrap();
// 之后就统一使用`Logger::global()`
}
延迟创建方式
use std::{sync::Mutex, collections::HashMap};
use once_cell::sync::Lazy;
static GLOBAL_DATA: Lazy<Mutex<HashMap<i32, String>>> = Lazy::new(|| {
let mut m = HashMap::new();
m.insert(13, "Spica".to_string());
m.insert(74, "Hoyten".to_string());
Mutex::new(m)
});
fn main() {
println!("{:?}", GLOBAL_DATA.lock().unwrap());
}