I'm trying to implement an OwnedMutexGuard
on top of std::sync::{Arc, Mutex, MutexGuard}
. I know about the prior art of tokio::sync::OwnedMutexGuard
, and lock_api::ArcMutexGuard
. But I'm using this for a data structure that is complex enough that I want to test it with loom
, and loom
only mocks std::sync
types. And I'd prefer to use standard library types anyway, for maintainability.
Recap of what an "owned mutex guard" is: Have a Arc<Mutex<T>>
, clone the Arc
, lock the Mutex<T>
to get a MutexGuard<'a, T>
, and store the Arc
along with the MutexGuard
. Then as long as your struct only ever drops the MutexGuard
before dropping the Arc
, the guarded T
is guaranteed to live as long as the guard, so lifetime bounds are unnecessary.
In theory.
In practice though, this is my best attempt:
pub struct OwnedMutexGuard<'a, T: 'a> {
guard: std::sync::MutexGuard<'a, T>,
handle: std::sync::Arc<std::sync::Mutex<T>>,
}
It needs an unsafe
std::mem::transmute
call when constructing, which is expected; to change the MutexGuard
's lifetime to 'a
, but it works.
It just really sucks to use, because of the lifetimes, when the whole point of this was to avoid having to write down a lifetime! I wish I could just have it be OwnedMutexGuard<T>
, with the 'a
silently swallowed because it's unnecessary.
All the 'a
is caused by MutexGuard<'a, T>
, which has a T: 'a
bound, which "infects" the surrounding struct, which when used infects the surrounding context, and so on, until every generic thing in my codebase is <'a, T: 'a>
.
I understand why this happens, and why a 'self
lifetime doesn't exist: it's usually helpful to know when a struct contains a reference, and that reference's lifetime is usually relevant. But in this case it's pure noise. There is no clarity or correctness gained from including it. tokio::sync::OwnedMutexGuard
doesn't have such a bound either.
Is there some unsafe
workaround to this problem? What sins can I commit to get a clean API?
I want to tell Rust to "sudo
ignore this T: 'a
bound, and let me write just OwnedMutexGuard<T>
; I promise I know what I'm doing".
Solutions I've considered:
-
Some
std::mem::transmute
incantation? I don't know how to phrase it, because I still need to at some point state the type I'm transmuting to, likeMutexGuard<'?, T>
, and that'?
needs to come from somewhere, and that somewhere is nowT: '?
, and so is everything that touches it. Same problem. -
Omit the lifetime by making it
'static
. But thenMutexGuard<'static, T>
requiresT: 'static
. Which is equally "infectious" in the codebase, and restricts use. -
Give up on my dreams of
std::sync
-purity and easy maintenance. Uselock_api
and replace this withlock_api::ArcMutexGuard
. Then vendorloom
as a git submodule, and manually merge this old PR to supportlock_api
. And maintain that, forever.