Acessing data from a global struct, gives error "borrowed value does not live long enough"

157 views Asked by At

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

1

There are 1 answers

3
Matthieu M. On BEST ANSWER

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)
}