In my current project there is a lot inter dependency between different structs, so instead of having Arc<Struct>
as members for each of the dependent Structs
. I thought of using Singleton Design Pattern to get the instance of that Struct
in the impl
block. This is a sample code that I've written. I would like to know if this code is safe, efficient and also a good way of modularization of the code.
use std::sync::{Arc, Mutex};
use lazy_static::lazy_static;
use std::thread;
struct A {
a: Mutex<i32>,
}
lazy_static! {
static ref A_OBJ: Arc<A> = Arc::new(A::new());
}
impl A {
fn new() -> A {
A {
a: Mutex::new(0),
}
}
pub fn print(&self) {
let mg = self.a.lock().unwrap();
println!("{}", mg);
}
pub fn increment_a(&self) {
let mut mg = self.a.lock().unwrap();
*mg += 1;
}
pub fn get_instance() -> &'static Arc<A> {
&A_OBJ
}
}
fn main() {
println!("Hello, world!");
let tjh = thread::spawn(|| {
println!("inside spawned thread");
let a_obj = A::get_instance();
a_obj.print();
a_obj.increment_a();
a_obj.print();
});
let a_obj = A::get_instance();
a_obj.print();
a_obj.increment_a();
a_obj.print();
let _ = tjh.join();
}
1 Answer 1
Firstly, you don't need use Arc
. You can just do:
lazy_static! {
static ref A_OBJ: A = A::new();
}
pub fn get_instance() -> &'static A {
&A_OBJ
}
lazy_static
ensures that A_OBJ
lives for the 'static
lifetime. There's no point in doing Arc
on top of that.
Is it a good way to modularize code? That's somewhat debatable, but I don't think so. As I see it, a singleton is just a global variable. Sometimes it does make sense to use a global variable, but generally not.
What approach do I take instead?
- Don't create unnecessary objects - if it doesn't have state it should just be a function
- Don't store dependencies on struct - any dependencies should be passed as function parameters
- Create a
Context
,Application
,Server
or similar struct to hold the complete state of your application. Any function needing access to the whole system should take a reference to it as a parameter.