Java has a particularly inelegant and ugly solution here involving declaring what types of exceptions might be thrown. But that still doesn't change the fact that changing that list is an API change, it just makes it more explicit.
So if you care about API stability (and I firmly believe that no solution that ignores API stability is "elegant" - it is at best "cute"), you're basically required to catch the vast majority of exceptions your own dependencies generate and translate them to your own exception types. You'll need to make a MyHTTPLibCertificateValidationError, unwrap the contents of OpenSSLValidationError, and put them in the new object, or you can never switch away from OpenSSL without an API break. And you want your dependencies to follow the same discipline.
At that point, as I said above, why use unwinding? None of the exceptions in your program can safely pass more than one level of the call stack at a time; each level has to explicitly approve raising it another level or wrap the exception in its own type (or handle it). The only ones that can really unwind are standard library ones like OutOfMemoryError that are expected to go all the way up the call stack to the top of the program or at best the top of the current request, print or otherwise log a backtrace, and abort the entire thing in progress - i.e., exceptional conditions. Exceptions for expected conditions are a different thing entirely, precisely because you don't want unwinding, you want step-by-step propagation.
This has nothing to do with efficiency. This has to do with correctness and robustness.
And you get your syntactic elegance with a library for translating and wrapping error objects, like https://docs.rs/error-chain , combined with syntax for immediately translating and returning errors from dependencies, like https://doc.rust-lang.org/book/second-edition/ch09-02-recove... .
Some compilers even won't let you use them without turning on full-blown RTTI(or doing it implicitly for you) which is even worse.
We've long since past the time that we have to worry about such concerns. "Exceptions for exceptional errors" just means that you have to write code that both cares about exception safety and propagates error codes from subroutines. It's the worst of both worlds.
Just use exceptions for all errors. It's elegant.