0

I store a Params structure into a SERVICES singleton. With the get_params() function, I get the Params structure back, but we must clone the structure's items in order to use it.

use lazy_static::lazy_static; // 1.4.0
use std::collections::HashMap;
use std::sync::Mutex;

#[derive(Debug)]
pub struct Params {
    pub verbose: bool,
    pub config_file: String,
}

lazy_static! {
    #[derive(Debug)]
    static ref SERVICES : Mutex<HashMap<&'static str, Params>> = Mutex::new( {
         HashMap::new()
    });
}

// OK
pub fn get_params() -> Params {
    let serv_map = &SERVICES.lock().unwrap();

    let cf: String = serv_map.get("params").unwrap().config_file.clone(); // clone here :(
    let verbose = serv_map.get("params").unwrap().verbose;

    Params {
        verbose,
        config_file: cf,
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let params = Params {
        verbose: true,
        config_file: "/user/toto.yml".to_string(),
    };

    {
        SERVICES.lock().unwrap().insert("params", params);
    }

    let pp = get_params();

    if pp.verbose {
        dbg!(&pp.config_file, pp.verbose);
    }
    Ok(())
}

playground

I would like to be able to read the Params data from the singleton, without cloning, using a routine like:

/*
 Cannot compile
*/
pub fn get_params_ref<'lt>() -> &'lt Params {
    let serv_map: &'lt HashMap<&'static str, Params> = &SERVICES.lock().unwrap();

    let pp = &serv_map.get("params").unwrap();

    pp
}

I get:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:22:57
   |
21 | pub fn get_params_ref<'lt>() -> &'lt Params {
   |                       --- lifetime `'lt` defined here
22 |     let serv_map: &'lt HashMap<&'static str, Params> = &SERVICES.lock().unwrap();
   |                   ----------------------------------    ^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
   |                   |
   |                   type annotation requires that borrow lasts for `'lt`
...
27 | }
   | - temporary value is freed at the end of this statement

I understand why Rust cannot compile this, but is there a solution to get an immutable reference to the Params structure?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
the duck
  • 375
  • 3
  • 13

1 Answers1

2

As written, you cannot return references to elements of SERVICES because the compiler doesn't know how long those will live: another part of your code could remove a service!

Assuming you only ever add elements to SERVICES but never remove any element then because they are global it's OK to leak them:

Change the definition of Params to

#[derive(Debug)]
pub struct Params {
    pub verbose: bool,
    pub config_file: &'static str,
}

And initialize your parameters' config_file using Box::leak(String::from("foo").into_boxed_str()).

Leaked memory will be reclaimed by your OS when your program shuts down.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
mcarton
  • 27,633
  • 5
  • 85
  • 95
  • I have followed your very neat advice here : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=154efe8819a95b9ec14a599950c727d5 It worked and it's a better solution. But I still have to create another "Params" struct everytime I call the get_params() routine. Is there a way to make the params struct static and return it as a ref ? – the duck Mar 25 '20 at 08:46
  • Don't bother: A struct with a boolean and a reference is very cheap to copy. – mcarton Mar 25 '20 at 08:56
  • Actually, I pushed your Box technique and put the params in a Box too and...it worked :) : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8f88b33064beb8f0df683a52bab8e114 Maybe I can simplify the code but the idea is here. Thanks a lot. – the duck Mar 25 '20 at 09:06