Ownership puzzle involving global

Hi,

I have this issue while factorising a piece of code to make a function more readable but I can't get my head around this error:

I'm believing it should be doable but malaxing the code doesn't led me anywhere yet

It is not possible to directly return a reference to something behind a mutex. If this were allowed, and there were no MutexGuard in the picture, then the mutex wouldn't be able to track how long it's used for and when it can be unlocked. So, no, this shouldn't be doable. You should instead return the mutex guard.


Also, if you are (correctly) using Lazy for storing globals, you shouldn't need any unsafe at all for this.

1 Like

Don't mind me, I think that a Mutex::get_mut() instead of Mutex::lock() would do the trick

Even with get_mut() instead of lock()?


Without unsafe, rustc is unhappy, what am I missing?
EDIT: rust-analyzer is unhappy, rustc is

Even more so, when you have a global. Shared mutability is forbidden, and if you don't actually perform the synchronization, then you can trivially invoke get_value() twice with the same key, causing aliasing &muts (already instant UB!) as well as a race condition. static mut is an anti-pattern and it should never be used, basically.

Even if it's protected with a Mutex?

If you get_mut(), and not lock(), then it's not protected.

So maybe I'm misreading something here: Mutex in tokio::sync - Rust

The get_mut() method is for when you can prove at compile time that you're the only one who is accessing the mutex. In that case, there's no reason to lock the mutex. You can't use it otherwise.

The documentation of get_mut says:

the mutable borrow statically guarantees no locks exist.

So it's explicitly saying it's not locking, which is wrong when you have a mutable global.

Ok, I was understanding "no lock needed", I guess I need to get rid of this global then

But rustc will not check that for me, will it?

Of course it will! Aliased mutability and race conditions are the exact reasons why accessing a mutable global is unsafe.

2 Likes

also, you cant just use unsafe at random places to make the compiler happy. unsafe is reserved for when you can guarantee that the operations inside it cannot cause safety issues

1 Like

Rustc will check that you're the only one who can access the mutex when using get_mut unless you use unsafe to incorrectly create a mutable reference to the mutex.

3 Likes

thanks @alice and @H2CO3, using get_mut() with no unsafe (I need to raise an issue for rust-analyzer) and rustc is happy! :smiley:

There were multiple, other problems with your code, e.g. the incorrect (too short) scope of the ctxs variable, or the "check if key exists otherwise insert" anti-pattern which doesn't work and should be expressed using the Entry API.

Here's the fixed code:

static SRV_CTXS: Lazy<Mutex<HashMap<String, MyType>>> = Lazy::new(Mutex::default);

async fn get_value(key: &str) -> MappedMutexGuard<'static, MyType> {
    MutexGuard::map(
        SRV_CTXS.lock().await,
        |map| map.entry(key.to_owned()).or_insert_with(MyType::default)
    )
}
3 Likes

Something must be still wrong there; it's not possible to access a global mutably without unsafe.

yes, there was another error that was masking the rest :frowning: need more work

Just use the code I posted above.

2 Likes