"Many commands, one piece of memory, possibly at the same time. The lock is what stops them from stepping on each other."
Why You Can't Just Mutate
Managed state hands you a shared reference, and Rust forbids mutating through a shared reference — because two commands could run concurrently and corrupt the data. The safe way to allow mutation is interior mutability: wrap the changeable data in a Mutex<T>. To change it, you lock() the mutex, which gives you exclusive access until the lock guard drops. Any other command that tries to lock waits its turn. No torn writes, no races — enforced.
Lock, Mutate, Release — Fast
Hold the lock for as little time as possible. Lock it, make your change, let the guard drop (often just by ending the scope), and you're done. A lock held too long serializes your whole app on that one mutex. For state that's read far more than written, a RwLock lets many readers in at once and only blocks for writes — a better fit for caches and config that occasionally updates.
The Async Trap (Again)
Remember the Send rule from the bridge track: a std::sync::Mutex guard must not be held across an .await — it won't compile in an async command, and even if it did, it would risk deadlock. Either drop the guard before awaiting (lock, copy what you need, unlock, then await) or use an async-aware mutex like tokio::sync::Mutex. For most commands, the lock-copy-unlock pattern is simplest and fastest.