let client_guad = client.lock_owned().await;
<...>
tokio::task::spawn(async move {
if try_do(|| client.try_open()) {
*client_guard = Some(client);
}
});
to use Mutex I should put Client into Mutex<Option<Client>>, do unnecessary locking and unwrapping. If I inline function it work as expected. I can use macros instead of function, but macros, imho, not intended to use for such purposes.
You can always wrap the more complex mutability with an interior Mutex. I've been doing this for all my tokio work.. I don't like it, but it gives me the cleanest looking fn main. I typically keep refactoring when I find alternate ways.
In tokio you'll have to decide whether you want to use tokio-Mutex or std-Mutex, as you should NOT hold the mutex across sub-async boundaries; but if you have a 2 step IO operation it's better to use tokio-Mutex than anything else.. Make sure ANY IO (and definitely any async/await barriers) you release and re-acquire the mutex. Personally I've been putting everything in dedicated scopped threads, as it's faster and easier to reason about.
Why do you think the locking is unnecessary? The runtime is free to move your async code to arbitrary threads, so if you want shared mutability, you need to synchronize it.