Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I find result types to be much easier to understand and work with than exceptions. Result types can be handled by the type system, even when you have checked exceptions in java, there are still exceptions that aren't checked, and the syntax for the checking becomes monstrous.


The anti-pattern I've seen in dysfunctional enterprise development shops (i.e. most of them) is that checked exceptions mean exceptions that "we'll never have" get buried lower in the stack; so code can fail silently and continue running just to avoid the monstrous checking code and propagation of exception type declarations up the call stack.

I don't see how the Rust approach would avoid this fate but I doubt it will ever be used in these contexts to begin with.


I think that part of it is that the idea of a Result type being normal will help prevent much of the cruft and burying we see with exceptions. I also feel like handling Result types is more natural than exceptions.

First, you _have_ to do it, even if that means a try! and passing the buck. The syntax for this isn't as monstrous as it is for checked exceptions as well.

Second, it feels more like a natural code-flow, not the break that exceptions provide.

Third, it's useful for more than just "Exceptions". Coupled with Optional types, it provides a more expressive way of not-hapy-path-code where exceptions just feel heavy handed. For instance https://docs.python.org/3/library/stdtypes.html

     d[key]

        Return the item of d with key key. Raises a KeyError if key is not in the map.
The key not existing isn't really exceptional. A proper optional, union, or result type handles this case much more easily.

In sum, I think the expressibility of Result and Option, along with a more natural flow for handling them will make working around them less tempting/viable/easy to pass over in a code review.


You don't have to convince me of the value of Result types, I prefer them too. I've only written a couple thousands of lines of Rust but tens of thousands of Haskell which rely on the same concepts (though its nicer with do notation). I don't have faith in the masses though and right now there is a strong selection bias in Rust that means only people concerned with quality and correctness are using it in the first place.

I think twenty-five years ago there were similar hopes and dreams for Java checked exceptions.


The big problem with Java checked exceptions was that the language was simply not flexible enough in other areas to handle higher-order stuff. For example, quite often you want to define something like "this method takes object X, and throws everything that X.foo() can throw, and also E". But there's no way to express it in Java. So the moment you start doing any sort of HOF-like stuff - even as simple as event handlers - you have to struggle with checked exceptions.

This is not a problem in Rust.


> I think twenty-five years ago there were similar hopes and dreams for Java checked exceptions.

Yeaaaaaaah. I remember being a very strong supporter of checked exceptions before I got into a giant codebase and saw how not having speced a sane exception type can cause lots of pain and some really long declarations or dropped exceptions.

   catch (Exception e) { throw new RuntimeError(e); } 
:(

You're right, I guess time will tell.


Exceptions in Python are used rather differently than in most other languages. Their overhead is much lower than exceptions in e.g. Java or C++, so they're routinely used to indicate expected conditions, like missing key in dictionary, or end of iterator. Since the language is dynamically typed in the first place, there's no concern about it effectively circumventing the type system.

That said, dict also has get(), which lets you specify the default value if key wasn't found (and the default default is None).


> fail silently

No. They are usually logged. The difference is that with exceptions, the logging will occur at a higher level and be done uniformly while in Rust you'll have to explicitly thread the error through the call stack manually to log it at a higher level.


On that point, does that mean Rust libraries tend to have logging hooks or are all errors emitted by Result's?

What about non-fatal errors such as retries in a GUI library that loads an image from the web, how are they logged or otherwise propogated to the developer?


Errors in Rust are just normal values, so there's nothing special you have to do. You just write your retry logic, presumably after inspecting the kind of error that occurred.


I'm aware of how Result works, I've written a little Rust. I was wondering how Rust libraries specifically deal with non-fatal error handling in parts of the library that are abstracted away from the library consumer, such as my example of a GUI library that has retry behavior that is not exposed to the user.

Is there currently a convention for dealing with such "encapsulated but interesting" errors occuring in Rust libraries?

It seems obvious to me that a web framework would have a logging hook but non-obvious how that API would function; would it call a logging callback with a severity and a string? Just a string? Or an error message and some kind of "related data" (such as a stack trace or relevant structs) container?

It doesn't seem obvious to me to log only text in the context of a GUI library. I'm thinking of building a native cross-platform GUI library in Rust (borrowing concepts from IUP[0] but adding more typing) and I feel like there would be value in having structured data as part of the nonfatal error interface, so I'm curious if there are existing patterns to learn from.

[0] http://webserver2.tecgraf.puc-rio.br/iup/


Ah, I see.

For logging, the `log` crate[1] provides the interface that libraries can use, which defines macros for each log level.

For partial success/failure, I actually don't think there is much convention. On the one hand, you might consider logging as sufficient enough, depending on your use case. In some cases, I have adopted a form of partial errors. That is, instead of:

    fn foobar() -> Result<Value, Error> { ... }
I use

    fn foobar() -> (Value, Option<Error>) { ... }
You can see an example here: https://docs.rs/ignore/0.1.9/ignore/gitignore/struct.Gitigno... And in particular, the error type is a recursive structure, which permits it to store an aggregation of errors: https://docs.rs/ignore/0.1.9/ignore/enum.Error.html

Since this isn't something people need too often, the syntactic overhead of this approach is considerably clunkier, so I definitely wouldn't want this to be a pervasive part of a library. Nevertheless, if you can get both a success value and an error value, then your return type is inherently a product, not a sum, which is at odds with the `Result` sum.

[1] - https://docs.rs/log/0.3.7/log/


Implicit return codes (e.g. return int, -1 means error, 0+ means OK) are equivalent to unchecked exceptions. Explicit return codes, where you must process them or compiler will yell at you are equivalent to checked exceptions. I think, that checked exceptions are a good idea, but they must be improved. E.g. Rust have syntax for almost implicit converting one error to another and return it; checked exceptions could use similar approach, so you can declare another exception in your "throws" cause and compiler'll generate conversion code for any unhandled checked exception.

Anyway for me exceptions are way easier to work with, than return codes.


Yes, exceptions are better than return codes, but I would argue Result types are better than exceptions. Checked exceptions in Java have their issues. Exceptions in C++ are odd beasts (though that's getting better, they're still not checked and therefor basically anything that isn't noexcept can throw them (oh wait! noexcept can throw! it just crashes immediately)).


That equivalence is false. An unhandled exception bubbles up. An unhandled return code is ignored. This was exactly one of the biggest arguments against return codes.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: