auto x = state.with([](auto& state) { return state.x; });
Becomes this:
auto x = state.locked()->x;
But it also creates this very accessible footgun:
auto& x = state.locked()->x;
Where it's way too easy to bind a reference to something that should be protected by the lock, but now isn't. So I'm not sure this is a great idea anymore.
> But it also creates this very accessible footgun:
Well, you said it yourself in the article though. There's always ways around the locking; C++ doesn't really give you the full ability to guarantee a field is locked when access. You'll need to either trust the users to some extent or use a style guide to disallow the pattern (I'd suggest only allowing use of auto x = state.locked() to avoid lifetime questions around state.locked()->x). You'd need to use compiler annotations to get any better.
Because now you're holding a reference to `x` which is supposed to be protected by a mutex, even after the mutex is unlocked.
With the lambda-only API, it's much harder to make this mistake, since a temporary reference like this will still go out of scope at the end of the lambda expression.
You specifically mentioned that this is a footgun:
> auto& x = state.locked()->x;
But I don't see how the reference here is gonna make a difference unless i am reading the lifetime of the lock here incorrectly. For example, this is perfectly fine right?
```
{
auto& x = state.locked()->x;
}
```
This will only be a problem if you have an outside struct that holds a reference
Holding the reference to a field that is protected by a mutex implies there is another thread out there that will race with your reference in either reading or writing it.
Even just a read is racy, as there is no “atomic read” of any size value if it is not already wrapped as atomic.