I am not that familiar with Rust, however every time I see it I am more and more impressed.
One thing that I don't see here but which I think it might be important is to grab the stack trace in the failure, or atleast the Filename / Line Number of where each Err is allocated. It will be really useful to be able to see the details for figuring out what went wrong, and following the stack trace is very important. With that I think the abstraction is 99%+ superior to exceptions, which is quite a step forward for the programming craft.
What really has to be guarded against though is that a lot of functions are going either have function signature that is much more verbose which leads programmers `cheating` and just `swallowing` the error by forcing the result out -OR- Return type inference might save the day (not sure if Rust has return type inference of not), however it doesn't solve everything, writing the return types on public functions pretty quickly becomes standard operating procedure, and then programmers are going to get lazy again and we are right back at checked exceptions `polluting` their interfaces and they might not want to deal with it.
interface Perfect {
def foo : Foo
def bar : Bar
}
interface NotPerfect {
def foo : Result[Foo, FooErr]
def bar : Result[Bar, BarErr]
}
Which of these do I implement first? Does the type system allow me to substitute a Perfect when a NotPerfect is required, or do I have to re-teach it every single time, that in fact, it's totally ok to do so (make a method `perfectCanBeUsedForNotPerfect(p : Perfect) : NotPerfect = new NotPerfect ....)`? Can I fix the issue for a limited set of cases and declare that Perfect implements NotPerfect and is therefore a subtype? Can I do similarly if I implement NotPerfect after Perfect was implemented (add an implicit conversion from Perfect to NotPerfect)? Can I just avoid the whole problem all-together with by-name subtyping (aka Point3{x,y,z} is considered a subtype of Point2{x,y} because Point3 has all the same named fields as Point2 with an extra one added) and informing the type system of the fact that Foo is a valid substitution for Result[Foo,_] because Result[A,B We static programmers will grumble on regardless, we pay our dues to the type checker because we believe its gives us structure and forces failure when while we write the code, not because we want to write boilerplate to teach it simple facts.
...
All in all this is really cool. From a Scala perspective it's a much improved version of Either (and now Try), and it's going to be supported by the standard library in Rust. Which is awesome.
Either itself basically is Scala saying, we don't have type disjuction, so here is a disjunction of 2 types which we will call Left and Right. And by convention Left is used to hold the error condition.
Because no one could remember that Left by convention meant error, and because it would by nice to gussy up what is held in the error type by having your errors be able to point a source error, Scala then later added a Try disjunction. Which is composed of two types Success[A](a:A) and Failure(e : Throwable).
The Success part is fine it's hard to mess that part up, it's just like Ok, having a uniform this just a wrapper around another type would be nice but, we suck that down as programmer business as usual, the Failure side however leaves a lot to be desired, by DESIGN the failure side cannot hold a failure and instead has to hold an exception, this is wrong, failure is a failure. Given the runtime on which Scala runs, the JVM, ignoring exceptions all-together is probably a worse evil. However it should have been made a special subcase of the Failure subclass rather than the only thing it can contain, or Try itself should have been the sub-implemenation of some more generic structure.
What to add a custom message to your failure? Ok make an exception that has a message in it.
Want to pass a Failure up the chain while adding your own error message for context? Ok make an exception then add the failure into it.
Want to fail without an exception? Ok make an exception and put it in the Failure.
Looks like Rust is moving towards a great implementation. This is something that has to be done in the standard library and has to be done well. The more I see of Rust as a language the more impressed I am, when is Rust going to compile to Javascript, oh never I see[1], well back to my ScalaJS cave then :)
1:https://news.ycombinator.com/item?id=4630403