RefCell: how to deceive borrow checker?



Here’s another question I’ve been spamming this forum recently :slight_smile:

I have global, thread-local context handle, and a local similar handle stored in some structure.

    pub fn enter(&mut self, message: isize) -> isize {
        use std::mem::{replace, swap};
        let mut frame = &mut self.0.context;
        // 0. Ret = Some(x), Frame = Some(y), Tmp = ???
        // 1. Frame -> Tmp, Frame = None, Tmp = Some(y)
        let tmp = replace(frame, None).unwrap();
        G_CONTEXT.with(|cell| {
            let mut ret = cell.borrow_mut();
            // 2. Ret <-> Frame, Ret = None, Frame = Some(x)
            swap(frame, &mut *ret);
            // 3. jump! Ret = Some(z), tmp = ???
            tmp.jump(&mut *ret, message)

My trouble comes near pt. 3. Accesses to G_CONTEXT may ‘nest’ to some degree. Please also note that jump will switch execution contexts. Thus, ret will be free before jump completes its job.
So I need to somehow ‘free’ ret in the middle of jump. Which isn’t possible - because jump is a piece of assembly code. Otherwise, I’ll get panic when doing borrow_mut in a nested ‘scope’.
So the question goes. Will it be Ok to &mut *ret as *mut _, return it from scope where retexists, then *mut _ as &mut _ back and pass to jump?


Why LocalKey::with is so restrictive?

If you need to do something that RefCell can’t express, you can always use UnsafeCell.


You could use drop. You might need to build a wrapper around it so that it can be called from the assembly code using the C ABI. This also means jump will need to take ownership of the whole Ref, which means also building a wrapper for Ref’s deref function.


Not the best idea IMO - to introduce some runtime abstraction where the logic scope is known and fixed. And even more, I don’t like this because it introduces leaking abstraction.

@eefriedman Thanks, I’ll try it. Seems spooky at the first glance, but in fact it’s what I need - a way to control scope access manually.