Struct object as service

Hi,
I'm learning already Rust. I'd like to create some kind of "global service" something similar to services in Symfony for PHP.
For example. I'm creating ConfigurationService struct which holds configuration data then I'm creating object for this. Now, somewhere in code I need to use variable for this config. How can I "inject", get access to it?

The "usual and easy" solution would be to wrap your configuration struct in a Arc<Mutex<Config>> and share that between structs.

But the way you worded your question makes it unobvious what actually is the problem you're having ? Do you want to use "dependency injection and inversion of control containers" ? That's an unusual pattern in Rust, but there are a few crates for that. On the other hand, simple composition is very frequent.

1 Like

Hi,
thank you for your answwer.
Yes, I thought about Arc<> also, to fire it on seperate thread. This won't be only configuration service but I'm going to set service for my each entity like UserService, ProductService etc. Each service has to manage data for endpoint, get from anywhere. Endpoint doesn't care from service will get the data.

I think that I have to change my point of view :slight_smile: because I was using services in Symfony (PHP) and there services where generated per request, in Rust I can create service onece while program starts.

My problem is/was that I didn't knew how to deal with services in rust and how can I manage it with Rust.

For example:

struct Config {
service: &str
data: &str
}


thread::spawn(|| {
    let config = Arc<Mutex<Config(service: "test", data: "data string")>>;
    }
});

When I start something like that the thread will end almost immediately. How to make this thread ending only when main program ends without using loop{}?

As an alternative to loop {}. The call to thread::spawn returns a JoinHandle. If you call its join method, you can wait for the thread to exit.

Sidenote: You should be using String rather than &str in that Config struct.

Ok, thank you. I can call .join() at the end of main. I think I'm starting to getting it :slight_smile:

Which method is better to run threads: thread::spawn or thread::Builder::spawn()?
I see that Builder::spawn() returns Result<> so I can handle errors.

Unless you want to give the thread a name, just use std::thread::spawn.

1 Like

Could you give me some hint how to access to variable in thread?

fn main() {

    // Thread for store configuration
    let config_thread = thread::spawn(|| {
        // There is no need for mutex, config will be not mutable
        let config = Arc::new(CoreConfig::new());
    });

    // Second thread for access config in first thread
    let second_thread = thread::spawn(|| {
        // how to get config from first thread
        println!("db type from config {}", config.get_dbdriver());
    });
    
    // or somwere in code to get config from thread


    // some sleep, job
    thread::sleep(Duration::from_secs(10));
    config_thread.join();
    second_thread.join();
}

You need to make a clone of the Arc<CoreConfig> for every thread that needs access. Don't worry, cloning an Arc does not clone the underlying value — that's the point of an Arc.

Note that you must clone it before spawning the thread. The call to clone can't be inside the new thread.

ok, so something lik this? is this correct?

fn main() {

    // main config
    let config = Arc::new(CoreConfig::new());

    // Thread for store configuration
    let config_for_thread = Arc::clone(&config);
    let config_thread = thread::spawn(move|| {
        // There is no need for mutex, config will be not mutable
        let config = config_for_thread;
    });

    // Second thread for access config in first thread
    let config_for_second_thread = config.clone();
    let second_thread = thread::spawn(move|| {
        // how to get config from first thread
        println!("db type from config {}", config_for_second_thread.get_db_driver());
    });

    // or somwere in code to get config from thread


    // some sleep, job
    thread::sleep(Duration::from_secs(10));
    config_thread.join();
    second_thread.join();
}

Yes, that's fine. When you make clones like that, you will be able to use it in the second thread as well.