Share object for whole application - "Global object"

Hi,
I'm new in Rust and I stuck with share my config in application.
In main function, I'm creating config object, whre config file is read and it returns my Settings object:

fn main() -> Result<(), Box<dyn Error>> {
//...
    let config = match gurita_core::get_config(matches) {
        Ok(settings) => Box::new(settings),
        Err(e) => {
            println!("Error: {}", e);
            std::process::exit(111);
        }
    };

// ....
// Here I need to call fuction from any module (this time from lib.rs) which
// needs some data from config but I don't want to pass confing into all
// function which needs config 

let res = example_function(); 
println!("{:#?}", res);

Ok(())
}

Now in my lib.rs

pub fn example_function() -> String {
  // here I need my config
  let ttl = config.jwt.ttl;
  // some logic

 String::from(result_of_logic)
}

How to share that data?

You can check out https://crates.io/crates/lazy_static crate.

Which helps creating global variable which needs heap allocation.

1 Like

I'm sorry, I've deleted my post instead edit it :frowning:

Thank you for hint. I've made it before main

#[macro_use]
extern crate lazy_static;

lazy_static! {
        static ref CONFIG: Box<Settings> = match gurita_core::get_config(gurita_core::get_matches_arguments()) {
            Ok(settings) => Box::new(settings),
            Err(e) => {
                println!("Error: {}", e);
                std::process::exit(111);
            }
        };
    }

fn main() -> Result<(), Box<dyn Error>> {

This compiles, but I'm trying use in other file but compiler says that my CONFIG is out of scpe :frowning:

error[E0425]: cannot find value `CONFIG` in this scope
  --> gcore/src/lib.rs:54:18
   |
54 |     let config = CONFIG;
   |                  ^^^^^^ not found in this scope

Why ? It's really that easy, add a function argument, this way the caller knows this function needs this to work. Easier to read, easier to test.

(instead of lazy_static there's this in nightly and the corresponding crate to use on stable)

2 Likes

I don't have good answer for it :confused: I also don't have much experience in rust but I want to change it. Lets assume, that I'm writting some "nested" module and one of its function needs some "global data" which are initialized in main function. And now I have to insert this global to each function/object which leads to this function in module.
main() config ->funct_mod1(&config) -> f_mod2(&config) -> ... -> nested_mod_funct(&config, arg1, arg2)
This looks complicated. I think it would be better to get config directry from last function.

pub fn nested_mod_funct() {
// I need config so I grab it
let config = global::get_my_config();
}

I'd like to create "global variable" which I'll be able to get it from any place in code and I don't know if it is possible.

Maybe there is better solution to share data betwen modules/crates?

Global state often has the illusion of simplicity, but then you're really hiding what your dependencies are. Passing arguments where needed is simple to do. If it's immutable configuration data, it's a matter of passing a reference &config everywhere it's needed.

3 Likes

If passing that configuration around manually feels clunky then I'd argue that's a good thing!

What you are experiencing is your code tell you that it's too coupled and that you are passing a lot of state around your application. You can definitely make the pain go away by using global variables, but the core problem remains.

5 Likes

If lazy_static doesn't suit your needs, perhaps consider turning the design inside out:

  • Have a struct that holds the configuration
  • Make your currently free functions methods on this struct
  • If you have other structs already which use the config in their methods, add the config type to those structs as well

Then instead of functions changing their behavior based on some outside state, you'll have a configuration context within which functions (methods) can run.

This sounds good what I need to achieve.
Could you give me some example?

Now, I already have struct which holds the configuration

fn main() -> Result<(), Box<dyn Error>> {
 // ...
    let config = match gurita_core::get_config(matches) {
     // ...
    };
// ...

this config is object Box<Settings> which has whole config.

Something like this.

Thank you very much, I think that I'm starting understatd it :slight_smile: My approach wasn't right. This example does't solve my problem. I don't need to implement new methods to my Settings but just read its data.

mod elsewhere {
    use crate::settings::Settings;
   pub fn print_settings() {
   let s = ???? // Here I need my example object
   prlintln!("{:?}",s);
   }
}

fn main() {
    use settings::Settings;
    use elsewhere::*;

    let example = Box::new(Settings::new("Hello, world!".into()));

}

In PHP we have static methods which can be use to share the same object. We can call it and if object is intitalized it returns it, if not it create new object.

class Settings {

    private static $db;

    public static function getInstance()
    {
        if (self::$db === null) {
            // Do some logic
            self::$db = self::new();
        }
        
        return self::$db;
    }
}

After this we have one and only one instance of class in whole application.
Can we do this in Rust?

That sounds like OnceCell. As the documentation says, it's pretty much the same as lazy_static! in this pattern.

1 Like

Thak you. It could be this :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.