These are the ways I know of to create singletons in Rust:
#[macro_use]
extern crate lazy_static;
use std::sync::{Mutex, Once, ONCE_INIT};
#[derive(Debug)]
struct A(usize);
impl Drop for A {
fn drop(&mut self) {
// This is never executed automatically.
println!(
"Dropping {:?} - Important stuff such as release file-handles etc.",
*self
);
}
}
// ------------------ METHOD 0 -------------------
static PLAIN_OBJ: A = A(0);
// ------------------ METHOD 1 -------------------
lazy_static! {
static ref OBJ: Mutex<A> = Mutex::new(A(1));
}
// ------------------ METHOD 2 -------------------
fn get() -> &'static Mutex<A> {
static mut OBJ: *const Mutex<A> = 0 as *const Mutex<A>;
static ONCE: Once = ONCE_INIT;
ONCE.call_once(|| unsafe {
OBJ = Box::into_raw(Box::new(Mutex::new(A(2))));
});
unsafe { &*OBJ }
}
fn main() {
println!("Obj = {:?}", PLAIN_OBJ); // A(0)
println!("Obj = {:?}", *OBJ.lock().unwrap()); // A(1)
println!("Obj = {:?}", *get().lock().unwrap()); // A(2)
}
None of these call A
's destructor (drop()
) at program exit. This is expected behaviour for Method 2 (which is heap allocated), but I hadn't looked into the implementation of lazy_static!
to know it was going to be similar.
There is no RAII here. I could achieve that behaviour of an RAII singleton in C++ (I used to code in C++ until a year a back, so most of my comparisons relate to it - I don't know many other languages) using function local statics:
A& get() {
static A obj; // thread-safe creation with C++11 guarantees
return obj;
}
This is probably allocated/created (lazily) in implementation defined area and is valid for the lifetime of the program. When the program terminates, the destructor is deterministically run. We need to avoid accessing it from destructors of other statics, but I have never run into that.
I might need to release resources and I want drop()
to be run. Right now, I end up doing it manually just before program termination (towards the end of main after all threads have joined etc.).
I don't even know how to do this using lazy_static!
so I have avoided using it and only go for Method 2 where I can manually destroy it at the end.
I don't want to do this; is there a way I can have such a RAII behaved singleton in Rust?