> 1. results must be handled explicitly, exceptions are forwarded automatically. This means I must deal with each result locally, even with a "forward up" to the caller. This means much complex and less readable code
Rust address this very concisely.
In Rust, "forward up if error" is one character, '?', and you can't forget to use it because it also unwraps the non-error value.
In Go on the other hand, "foward up if error" is a notoriously repetitive multi-line sequence. Some people like it because it forces every step of error forwarding to be explicit and verbose. Some people don't.
In Go I've worked with, there's a lot of these. People aren't avoiding error handling to save keystrokes. But occasionally I've found a mistake in Go error-forwarding that went unnoticed for years and would have been detected by Rust-style statically-typed Result, or any kind of exceptions, so I'm not convinced the Go-style explicitness really helps avoid error-handling bugs.
> 2. exceptions have a stacktrace, handled automatically for you. This means more precise location of where the error occurred. For the same reasons exceptions are heavier - as they keep more data AND is updated for each frame (when handled, and depends on the implementations)
That's true in practice, but it's a design choice, not a strictly required difference between exceptions and Result<T,E>.
In principle, the heaviness of a stacktrace can be eliminated in many cases, if the run-time (with compiler help) keeps track of whether an exception's catch handler is never going to use the stacktrace (transitively if the handler will rethrow).
Lazy, on-demand stacktraces are also possible, saved one frame at a time on error-return paths, and sharing prefixes when those have been generated already. These have the same visible behaviour as normal stacktraces, but are much more efficient when many exceptions are thrown and caught in such a way that the run-time can't prove when to omit them in advance.
In principle, the same things can be applied to Result<T,E> types: Take a stacktrace where the Result is constructed, and use the above mechanisms to keep it efficient when the stacktrace isn't used. In Rust, there are library error types that save a stacktrace, if you turn the feature on, but I don't think any of them automate their efficient removal when unneeded, so turning the feature on slows programs unnecessarily.