0

As per thread I've had the need to create a global non-mutable singleton to store some static data.

#[derive(Clone)]
struct RefData {
    atm_vol : BTreeMap<String,String>,
    delta_vol : BTreeMap<String,String>
}

impl RefData {
    fn singleton() -> RefData {
        static mut g_RefData : *const RefData = 0 as *const RefData;
        static G_ONCE : Once = ONCE_INIT;
        unsafe {
            G_ONCE.call_once(|| {
                let mut ref_data = RefData { atm_vol : (BTreeMap::new()),
                                             delta_vol : (BTreeMap::new()) };
                ref_data.init();
                g_RefData = mem::transmute(Box::new(ref_data));
                });

            (*g_RefData).clone()
       }
    }

    fn init(&mut self) {
        self.atm_vol.insert("xcu".to_string(),"XCU_USD_VOL_DT".to_string());
        self.delta_vol.insert("xcu".to_string(),"XCU_USD_VOL_SKEW_M".to_string());
    }
    // This doesn't work as singleton doesn't last long enough
    fn vol_handle(asset : &str) -> Option<&String> {
        RefData::singleton().atm_vol.get(asset)
    }
}


#[test]
fn test_refdata() {
    let t = RefData::vol_handle("xcu");
    println!("{:?}",t);

}

It's single threaded so I'm not using Arc/Mutex.

How can I get RefData::singleton() to last long enough to return a reference to the value thats in the btreemap

Community
  • 1
  • 1
Delta_Fore
  • 3,079
  • 4
  • 26
  • 46

1 Answers1

2

It's single threaded so I'm not using Arc/Mutex.

And that is, actually, the first issue.

Arc and Mutex provide more than just thread-safety, they also provide shallow copies, so that all SingletonReader share the same storage underneath, and therefore the lifetime of the storage is not linked to that of the SingletonReader.

Thus, you should rather return a &'static reference to RefData.

fn singleton() -> &'static RefData {
    static mut g_RefData : *const RefData = 0 as *const RefData;
    static G_ONCE : Once = ONCE_INIT;
    unsafe {
        G_ONCE.call_once(|| {
            let mut ref_data = RefData { atm_vol : (BTreeMap::new()),
                                         delta_vol : (BTreeMap::new()) };
            ref_data.init();
            g_RefData = mem::transmute(Box::new(ref_data));
            });

        &*g_RefData
   }
}

And now, this implementation will work:

fn vol_handle(asset : &str) -> Option<&String> {
    RefData::singleton().atm_vol.get(asset)
}

However, it is slightly skewed: lifetime inference makes it so that it is interpreted as:

fn vol_handle<'a>(asset : &'a str) -> Option<&'a String> {
    RefData::singleton().atm_vol.get(asset)
}

Which is perhaps not what you wish for, as the lifetime of the String really is 'static here, and therefore you should rather aim for:

fn vol_handle(asset : &str) -> Option<&'static String> {
    RefData::singleton().atm_vol.get(asset)
}
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Just seen your comment. Yep that's changed back, but it leaves me with the original problem of being able to return a reference to the contained value in a simple way. It seems messy as it stands – Delta_Fore Jun 25 '15 at 13:56
  • @Ronnie: I am sorry, I do not understand what you find "messy". It is necessary to annotate the return of `singleton()` with `'static`, but afterward you should not need it (lifetime elision should work on `vol_handle`). – Matthieu M. Jun 25 '15 at 13:58
  • 1
    @Ronnie: I've updated my answer to match, however in the future please avoid drastic changes to your question that invalidates existing answers, it is seen as rude and might lead to heavy head-scratching when future readers see a discrepancy between question and answer. Prefer asking a separate question if necessary. – Matthieu M. Jun 25 '15 at 15:06