2
\$\begingroup\$

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();
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

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?

  1. Don't create unnecessary objects - if it doesn't have state it should just be a function
  2. Don't store dependencies on struct - any dependencies should be passed as function parameters
  3. 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.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.