I have a scenario like this : Rust Playground
- I have a struct
Containerwith a lock that is protecting multiple members. I don't want RwLock per member. - I want to acquire this lock and then call another function
complex_mutationthat does some complex logic and mutates this struct. - The problem is that RAII guard
_guardfor lock acquires a reference and then calling another functioncomplex_mutationwhere&mut selfis passed results in multiple immutable + mutable references. - I cannot drop the guard before creating another mutable reference as that is what is providing the synchronization here.
- If I could just inline everything from
complex_mutationthis will work but that would be too ugly for a real world scenario.
This is a common pattern in C++. How do I solve this in RUST?
You seem to be confused about a few things. Let me try to clarify.
(), which is pointless.&mut selfthen locks don't help you. Locks are one of many mechanisms that allow you to get an exclusive reference (&mut) from a shared reference (&). The ability to write to a value when you start with a shared reference is called "interior mutability" and it is implemented byRwLock, and also byMutex,RefCell, etc. If you are starting with an exclusive reference already, interior mutability is redundant.With that out of the way, what you want to do is put the data in your type into the lock. You can do this with a tuple, but a better idea would be a private "inner" type, like this:
Now, you want to take
&selfinstead.If you have methods that need to operate on the data in
ContainerInnerbecause you call them from multiple locations, you can simply put them onContainerInnerinstead, like this:Note that if you have a
&mut selfthen you don't actually need to lock;RwLock::get_mut()allows you to get a&mutto the inner value if you have a&mutto the lock without locking because having an&mutto the lock is a static guarantee that the value isn't shared.If it helps you conceptually,
&and&mutwork kind of likeRwLock'sread()andwrite()respectively -- you can have many&or a single&mut, and if you have a&mutyou can't have a&. (This is somewhat simplified, but you get the idea.) Unlike with runtime locks, the Rust compiler checks that these are used correctly at compile time, which means no runtime checks or locks are needed.So you could add a method like this:
This is exactly the same as the other method except we take
&mut selfwhich allows us to useget_mut()in place ofwrite(), bypassing the lock. The compiler will not let you call this method unless it can prove the value isn't shared, so it is safe to use if the compiler lets you use it. Otherwise, you need to use the other method, which will lock.