Good post. Not capturing a stack trace is a Rust specific thing -- this is easy to do in Scala, for example, and still maintain the benefits of monads + algebraic data types. I'm sure Rust will get similar utilities with time.
The main issue I have with this technique is the higher mental overhead when getting started. That goes away with a bit of practice.
Scala gets stack traces automatically from the JVM though, along with much of its debugging support. The Rust team will have to do this all by themselves, being a natively compiled language and all.
You can get stack traces on panics by running with RUST_BACKTRACE=1.
I don't know that it makes much sense on Error objects though, there's no requirement that they're even able to store backtraces, and collecting backtraces any time an error object is created would be really expensive and would make the runtime a dependency of errors.
Most VMs put most of the overhead of stack creation at where the error is created, so it usually works out fine (throw is expensive, everything else is cheap). Stack traces are usually dependent on whatever debug symbols are loaded, so in the CLR you'll get huge blank spaces if an exception propagates through some dynamically generated or even native code.
Just printing stack traces is a distraction though. The only thing that is required that on an exception, the entire stack above the the exception site can be inspected in the debugger.
And how do you tell where an error originated without a panic being called? Is the programmer expected to just instrument the their code to put the panic in as soon as possible? Anyways, this is a well-known problem with monadic error handling, and one of the main reasons it doesn't get more widespread adoption.
> I'm sure Rust will get similar utilities with time.
Unlikely. Scala gets tracebacks for Try because the left arm stores a JVM Throwable, and throwables reify a stacktrace during initialisation.
For Rust, this would mean:
* Result's Err mandates an Error (currently has no trait bound at all)
* Errors must be able to store a traceback
* Error/Result requires rt in order to collect a stacktrace
* Rather than stack-allocating a struct or two, Err results now require significant heap allocation and data collection completely breaking existing usage patterns
I'm not suggesting `Err` stores a backtrace. I am suggesting that the programmer could choose to store a backtrace in the error objects they choose to store in an `Err`.
I don't do this in my Scala code because I rarely find I need a backtrace.
This will collect a stacktrace in the provided buffer, the result can then be stored in whatever object the developer wants (likely after decoding it to a String): http://is.gd/tOD8Ho
Note that you really don't want to be relying on this unstable feature, it's quite a hack at the moment and will very likely be moved out into an external crate.
As long as Err results are only created under exceptional circumstances, there should be no performance hit right? This is one significant reason why errors should get a path separate from normal return values, but that isn't the case in Rust (or monadic error handling in general, which doesn't really consider debugging).
The main issue I have with this technique is the higher mental overhead when getting started. That goes away with a bit of practice.