Lifetimes Errors

Hi Rust Community,

I am getting an error
lifetime may not live long enough returning this value requires that 1 must outlive 2

I want to return the reference to one of the requested properties from StableStates, so other function calls and through references can mutate, query, etc stable states properties

   pub struct StableStates {
        pub user_accounts: UserAccountsType,
        pub constants: ConstantsType,
        pub init: InitType,
        pub account_metrics: AccountMetricsType,
    }

    #[derive(Serialize, Deserialize)]
    pub struct State {
        #[serde(skip, default = "init_stable_states")]
        pub stable_state: StableStates,
    }

std::thread_local! {

    pub static STATE: RefCell<State> = RefCell::default();


}

pub enum StableStatesProperties {
    UsersAccounts,
    Constants,
    Init,
    AccountMetrics,
}

pub enum MemoryRef<'a> {
    UsersAccounts(&'a UserAccountsType),
    Constants(&'a ConstantsType),
    Init(&'a InitType),
    AccountMetrics(&'a AccountMetricsType),
}

pub fn get_memory_ref<'a>(stable_state_name: StableStatesProperties)-> MemoryRef<'a>{
    match stable_state_name {
        StableStatesProperties::UsersAccounts => {
            let state= STATE.with_borrow_mut( |s| &s.stable_state.user_accounts);
            MemoryRef::UsersAccounts(state)
        }
        StableStatesProperties::Constants => {
            let state= STATE.with_borrow_mut( |s| &s.stable_state.constants);
            MemoryRef::Constants(state)
        }
        StableStatesProperties::Init => {
            let state= STATE.with_borrow_mut( |s| &s.stable_state.init);
            MemoryRef::Init(state)
        }
        StableStatesProperties::AccountMetrics => {
            let state= STATE.with_borrow_mut( |s| &s.stable_state.account_metrics);
            MemoryRef::AccountMetrics(state)
        }
    }
}

Borrows can't escape the with_borrow_mut closure for soundness reasons. (If it was possible, you could call it once, hold onto the borrow, and call it again -- resulting in a &mut T that aliases your escaped borrow. That would be instant UB.)

A static Mutex<_> or such might be an alternative to your thread local, but I didn't attempt it.

Playground for others.

1 Like

I was able to get something close by storing RefMuts inside MemoryRef and leaking one copy of State per thread.

Ideally, the thread local would be Rc<RefCell<…>> instead (which would solve the leaking problem), but I couldn’t find a good¹ way to create a smart pointer that does all of these things at the same time:

  • Keeps the Rc alive,
  • Holds the RefCell’s lock, and
  • Maps the locked value to something else

Âą i.e. Without dipping into writing my own unsafe code


Edit: Attempting the unsafe route…

I haven’t run anything through Miri, so I have pretty low confidence that this is actually correct, but I tried to write the unsafe version anyway. This is more of an exercise for myself than a suggestion— Please don’t actually use this without a lot of review from people that know this corner of Rust better than I do.

To facilitate such a review, here’s the relevant unsafe bits that I added:

trait Opaque {}
impl<T> Opaque for T {}

pub struct RcRefMut<T:'static> {
    // NB: The 'static here is fake, and really points into `rc`!
    //     For safety, these fields MUST be in this order so that the drop sequence is correct.
    ptr: RefMut<'static, T>,
    rc: Rc<dyn Opaque>
}

impl<T> Deref for RcRefMut<T> {
    type Target=T;
    fn deref(&self)->&T { &self.ptr }
}

impl<T> DerefMut for RcRefMut<T> {
    fn deref_mut(&mut self)->&mut T { &mut self.ptr }
}

impl<T> RcRefMut<T> {
    pub fn map<U>(RcRefMut{ptr, rc}: Self, f:impl FnOnce(&mut T)->&mut U)->RcRefMut<U> {
        RcRefMut {
            ptr: RefMut::map(ptr, f),
            rc
        }
    }

    pub fn borrow_from(cell: &Rc<RefCell<T>>)->Self {
        let rc = cell.clone();
        let inner = Rc::as_ptr(cell);
        let ptr = unsafe { (*inner).borrow_mut() };
        RcRefMut { ptr, rc }
    }
}

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.