rustforced-unwrappingrust-axum

Rust Axum framework - is unwrap safe?


I am using Rust Axum as my web framework.

I am looking to use do mutable shared state in my route handlers. I came across this question:

https://github.com/tokio-rs/axum/discussions/629

which pointed to this example:

https://github.com/tokio-rs/axum/blob/dea36db400f27c025b646e5720b9a6784ea4db6e/examples/key-value-store/src/main.rs

I am concerned about the unwrap() at various places when reading and writing to the state:

let db = &state.read().unwrap().db;

state.write().unwrap().db.insert(key, bytes);

I thought unwrap() was a bad practice?

I also feel like there really won't be any situation (that I can think of) when the unwrap may fail. But maybe I am missing something? In what scenario would this unwrap fail?

Is it safe to use unwrap() in the above example context?


Solution

  • TL;DR: Yes, it is fine to unwrap() on Mutex and RwLock locking.

    std's Mutex and RwLock implement poisoning: if a thread panics while holding the lock (write lock for RwLock), the lock become poisoned. Any further attempt to lock will return Err(PoisonError). This is why the locking operations return Result.

    The reason for that is that data is assumed to have invariants that should be always held. Code may break the invariants temporarily, and restore them after that. But if the code unexpectedly panics, the invariants may remain broken, and further code that will inspect the data may see it in an invalid state and produce wrong results (or panic). This is the same reason as with the existence of the unwind safety traits.

    Normally, this is not a problem, because if the code panics, the code that called it will also panic (unwind or abort) and the data will be completely inaccessible. But there are two cases where it is a problem: when you catch the panic (the abovementioned unwind safety), and when you access the data from a different thread, because a thread that panics won't cause another thread to panic (in unwind mode). This is why the locks are poisoned in this case, to prevent you from seeing the broken data.

    Sometimes, you want to deal with the panic and restore the invariants or exit gracefully. But most of the time, when an other thread panics and made the data junk, you want to panic too without accessing it. This is what the unwrap() does: it panics in this case.

    Since it only panics if another thread panicked while holding the lock, it can only panic if there was already a panic. So even thought panics are generally discouraged in Rust, in this case they are fine.