先用一小段代码辅助说明结论(涉及多线程、多个可变引用下的实现)
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>> {
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()
}
}
}
- 用
Option<...>
作为静态变量来存储单例对象的原始全局指针,用get_or_insert_with方法来初始化单例对象 - 最严谨的方法是用
Arc<Mutex<T>>
或者Arc<RwLock<T>>
来持有单例对象;如果不需要单例对象的可变引用,直接用Arc<T>
即可;如果是单线程程序,Arc
可以可用Rc
替代
为什么不用Box而是Arc/Rc?
Box表示唯一的指针引用,不能clone。单例对象是要被多处代码所引用的,Box<T>
只能move不能clone,所以根本无法实现将一个Box<T>
作为共享指针。
不需要可变引用的情况
如果不需要可变引用,那就没有必要使用Mutex或者RwLock了。可以返回一个Arc<T>
或者&'static T
pub fn get_instance() -> Arc<Point> {
static mut POINT: Option<Arc<Point>> = None;
unsafe {// Rust中使用可变静态变量都是unsafe的
POINT.get_or_insert_with(|| {
// 初始化单例对象的代码
Arc::new(Point {x: 0, y: 0})
}).clone()
}
}
// 返回&'static Point
pub fn get_instance() -> &'staic Point {
static mut POINT: Option<Arc<Point>> = None;
unsafe {// Rust中使用可变静态变量都是unsafe的
POINT.get_or_insert_with(|| {
// 初始化单例对象的代码
Arc::new(Point {x: 0, y: 0})
});
POINT.as_ref().unwrap()
}
}