recover() exists. You're right, there's a stigma to it, because you're not supposed to use it unless you really need to (hence, no programmer confusion). It's supposed to be used for situations like:
- Catching panics before crossing an FFI boundary
- End-of-the-world situations like OOM where you want to still handle it somehow
- Ensuring that applications can recover from internal panics in libraries (though there should be little to no panics in the libraries anyway)
The stigma for recover is for using it where you're not supposed to; as a substitute for regular error handling. In this situation, you are supposed to, so the stigma doesn't apply.
The fact that it's not a first-class primitive seems mostly irrelevant to me. Rust does a lot of things in library functions and types, even our concurrency safety mechanisms are something that can be duplicated in a library. As long as it can be used, what does it matter?
The fact that you can set the panic handler at runtime is also irrelevant. If you want to catch panics, don't do that.