There are types FooWrapper
and BarWrapper
wrapping around remote types Foo
and Bar
. Foo
has a method bar
that creates a new instance of Bar
which borrows from itself. The FooWrapper
does the same for BarWrapper
. The implementation is straightforward so far:
struct FooWrapper {
foo: Foo,
}
impl FooWrapper {
fn new() -> Self {
Self {
foo: Foo::new(),
}
}
fn bar(&self) -> BarWrapper {
BarWrapper {
bar: self.foo.bar(),
}
}
}
struct BarWrapper<'a> {
bar: Bar<'a>,
}
struct Foo {
value: u32,
}
impl Foo {
fn new() -> Self {
Self {
value: 0,
}
}
fn bar(&self) -> Bar {
Bar {
value: &self.value,
}
}
}
struct Bar<'a> {
value: &'a u32,
}
fn main() {
let foo = FooWrapper::new();
let bar = foo.bar();
println!("{}", bar.bar.value)
}
However, it gets more complicated if FooWrapper
and BarWrapper
are using a std::sync::Mutex
to be able to be shared between threads. Here's FooWrapperMutex
and BarWrapperMutex
:
use std::sync::Mutex;
struct FooWrapperMutex {
foo: Mutex<Foo>,
}
impl FooWrapperMutex {
fn new() -> Self {
Self {
foo: Mutex::new(Foo::new()),
}
}
fn bar(&self) -> BarWrapperMutex {
let foo = self.foo.lock().unwrap();
BarWrapperMutex {
bar: foo.bar(),
}
}
}
struct BarWrapperMutex<'a> {
bar: Bar<'a>,
}
This won't work, because the instance of BarWrapperMutex
created in bar
would outlive the acquired std::sync::MutexGuard
. Storing the MutexGuard
in BarWrapperMutex
won't work either, because self-referential structs are not allowed.
How can I solve this properly?
If you don't want to (or can't) use parking-lot, you can wrap the guard:
impl FooWrapperMutex {
fn new() -> Self {
Self {
foo: Mutex::new(Foo::new()),
}
}
fn lock(&self) -> FooGuard {
FooGuard(self.foo.lock().unwrap())
}
}
struct FooGuard<'a>(MutexGuard<'a, Foo>);
impl<'a> FooGuard<'a> {
fn bar(&self) -> BarWrapper {
BarWrapper { bar: self.0.bar() }
}
}
fn main() {
let foo = FooWrapperMutex::new();
let lock = foo.lock();
let bar = lock.bar();
println!("{}", bar.bar.value)
}
Another alternative is to keep the mutex separate from the wrappers. This simplifies the wrappers and gives the user the choice of:
- Which type of mutex to use
- How much the mutex protects. e.g. maybe the user needs to keep thread-shared state consistent with Foo, so needs to keep that state under the same mutex.
fn main() {
let foo = Mutex::new(FooWrapper::new());
let lock = foo.lock().unwrap();
let bar = lock.bar();
println!("{}", bar.bar.value)
}
Thanks for your replies.
Mutex guard mapping seems ideal, but for now I'd like to stay within std if possible.
Making a wrapper for the guard works in this case and makes sense. However, I have a more specific case: FooWrapperMutex
and BarWrapperMutex
are specific implementations of traits AWrapper
and BWrapper
that just so happen to need locking because of their usage of Foo
and Bar
:
use std::sync::Mutex;
trait AWrapper {
type BWrapper<'a> where Self: 'a;
fn b(&self) -> Self::BWrapper<'_>;
}
trait BWrapper {
fn value(&self) -> u32;
}
struct FooWrapperMutex {
foo: Mutex<Foo>,
}
impl FooWrapperMutex {
fn new() -> Self {
Self {
foo: Mutex::new(Foo::new()),
}
}
}
impl AWrapper for FooWrapperMutex {
type BWrapper<'a> = BarWrapperMutex<'a>;
fn b(&self) -> BarWrapperMutex {
let foo = self.foo.lock().unwrap();
BarWrapperMutex {
bar: foo.bar(),
}
}
}
struct BarWrapperMutex<'a> {
bar: Bar<'a>,
}
impl BWrapper for BarWrapperMutex<'_> {
fn value(&self) -> u32 {
*self.bar.0
}
}
struct Foo(u32);
impl Foo {
fn new() -> Self {
Self(0)
}
fn bar(&self) -> Bar {
Bar(&self.0)
}
}
struct Bar<'a>(&'a u32);
fn main() {
let a = FooWrapperMutex::new();
let b = a.b();
println!("{}", b.value())
}
Because of this, I can't make locking a part of the API.
Sorry I didn't mention that, I just thought it's not important for a minimal example. Sorry for the naming too, it starts getting more confusing now because I didn't think there will be a need to expand on it...
Same thing goes for a separate mutex - it makes sense in this case, but does not work for the more specific one.
I'm going to make a guess on your ultimate goal: create a set of traits which mirror object relationships whether they use locking or not. If this is accurate, then you probably need to model guards in your traits.
If your goal is different, create set of objects which implement locking without exposing that locking to the traits, then you might be stuck.
use std::sync::{Mutex, MutexGuard};
trait AWrapper {
type AWrapperGuard<'a>: AWrapperGuard
where
Self: 'a;
fn lock(&self) -> Self::AWrapperGuard<'_>;
}
trait AWrapperGuard {
type BWrapper<'a>: BWrapper
where
Self: 'a;
fn b(&self) -> Self::BWrapper<'_>;
}
trait BWrapper {
fn value(&self) -> u32;
}
struct FooWrapperMutex {
foo: Mutex<Foo>,
}
impl FooWrapperMutex {
fn new() -> Self {
Self {
foo: Mutex::new(Foo::new()),
}
}
}
impl AWrapper for FooWrapperMutex {
type AWrapperGuard<'a> = FooWrapperMutexGuard<'a>;
fn lock(&self) -> Self::AWrapperGuard<'_> {
FooWrapperMutexGuard(self.foo.lock().unwrap())
}
}
struct FooWrapperMutexGuard<'a>(MutexGuard<'a, Foo>);
impl<'a> AWrapperGuard for FooWrapperMutexGuard<'a> {
type BWrapper<'b> = BarWrapperMutex<'b> where Self:'b;
fn b(&self) -> Self::BWrapper<'_> {
BarWrapperMutex { bar: self.0.bar() }
}
}
struct BarWrapperMutex<'a> {
bar: Bar<'a>,
}
impl<'a> BWrapper for BarWrapperMutex<'a> {
fn value(&self) -> u32 {
*self.bar.0
}
}
struct Foo(u32);
impl Foo {
fn new() -> Self {
Self(0)
}
fn bar(&self) -> Bar {
Bar(&self.0)
}
}
impl AWrapper for Foo {
type AWrapperGuard<'a> = FooGuard<'a>;
fn lock(&self) -> Self::AWrapperGuard<'_> {
FooGuard(self)
}
}
struct FooGuard<'a>(&'a Foo);
impl<'a> AWrapperGuard for FooGuard<'a> {
type BWrapper<'b> = Bar<'b> where Self:'b;
fn b(&self) -> Self::BWrapper<'_> {
self.0.bar()
}
}
struct Bar<'a>(&'a u32);
impl<'a> BWrapper for Bar<'a> {
fn value(&self) -> u32 {
*self.0
}
}
fn test<'a, T: AWrapper>(a: &'a T) {
let guard = a.lock();
let b = guard.b();
println!("{}", b.value())
}
fn main() {
test(&Foo::new());
test(&FooWrapperMutex::new());
}
Your guess is correct, I'm not simply implementing some traits, I am designing them, and they are supposed to work for both cases, locking or not. I'll mark this as solved, thanks for your help.