6 ms·
I am a full time Go developer learning rust on the side for more low level things, and one of my side projects is writing an OS. Rust's error handling really f
by icxa 7y ago
I am a full time Go developer learning rust on the side for more low level things, and one of my side projects is writing an OS.
Rust's error handling really feels the exact same way to Go's in my opinion, the only difference is, so far in my admittedly newcomer viewpoint, using unwrap to get to the error value, or returning an error value and doing an if err != nil check.
This isn't to say anything other than agreement that I indeed like this style. I like to handle my errors as values and program around them.
The thing I don't get is why all the bikeshedding and hate around "if err != nil" spam I seem to have seen here often and on other blogs. Go seems to get this "criticism" (I don't really believe it is a valid critique, for what it is worth) heaped onto it plentifully, while Rust seems generally immune to it, otherwise praised for the approach -- but it is the same approach! In fact you even have a builtin panic func in go!
Can someone tell me what I may be missing?
- andrewjf 7y agoThe best parts of rust's error handling are the try!() macro (aka `?`), as well as From/Into error types allow the compiler to wrap types or convert between error types. This eliminates almost all of the go boilerplate `if err != nil` and you only handle the error where you actually need to with pattern matching and destructuring, instead. It is much nicer. Go's error handling is pretty primitive, in my opinion.
- deleted 7y ago[deleted]
- aksx 7y ago>The best parts of rust's error handling are the try!() macro (aka `?`), Go's way of providing try!() macro is less magical but almost as useful.[1] > From/Into error types allow the compiler to wrap types or convert between error types error is an interface in Go which can be easily cast/checked for the underlying type. > Go's error handling is pretty primitive I wouldn't call it primitive, I would call it simple. I like the comparison i read on a blog on HN. Rust is the 'new' C++ and Go is the new C [1] https://blog.golang.org/errors-are-values https://blog.golang.org/errors-are-values
- xfer 7y ago> error is an interface in Go which can be easily cast/checked for the underlying type. Yeah it's done during runtime and the compiler won't be able to help you with it if you fail to do exhaustive type checking. It's a problem anytime you refactor your code. ADTs and pattern matching is pretty much the bare minimum language feature i expect from any statically typed language.
- joshmarlow 7y agoYou can do `.unwrap()` or `.expect(..)` to get your values, and in that respect, it's not that different from working in a language with `null`. But you can actually almost totally avoid calling `unwrap`/`expect` in Rust code by doing pattern matching. ` match x { Some(value) => <DO SOMETHING WITH VALUE> None => <HANDLE THIS ERROR PATH>, }`. If you always do that, then you've got the compiler reminding you to handle the case where it is `null`. That means that you never hit the equivalent of a null exception. That being said, I still find myself doing some `unwrap` and `expect` (particularly in tests). But relying on matching most of the time as a first line of defense will prevent the equivalent of a null exception in the bulk of your code.
- mixedCase 7y agoSince Rust supports generics and algebraic data types, you can clearly denote on your return type what kind of errors can be expected and include useful data pertaining to each one, with the compiler making sure everything in its place; as opposed to casting from the error interface in a brittle manner prone to breaking on changes with no help from the compiler. Since Result is also a functor in practice, you can chain modifications to the happy path without having to check errors when you don't need to and without having to write as much boilerplate.
- ohazi 7y agoA lot of the Rust developers I've spoken with don't really consider using unwrap everywhere the way that Go programmers use "if err != nil" to be proper error handling. Most of them consider it an ugly hack to be used when playing around or testing something. The value that most people see in Rust's error types is in error transformation and propagation, which let you do things like correctly handling edge cases when using closures without cluttering up the logic of the success case. If you are going to compare unwrap/expect to "if err != nil" -- Rust's advantage is that it forces you to at least pretend to handle the error, while Go will happily keep chugging along even if you never inspect err.
- ilitirit 7y ago> Rust's advantage is that it forces you to at least pretend to handle the error I'm not sure I would call this an advantage. See: Handling of Checked Exceptions in Java.
- zmmmmm 7y agoSort of seems even worse in that there is no built in way to propagate the error if you don't want to handle it. At least with Java you just added the exception to the throws declaration of your function if you didn't want to handle it. Here you have to do something about it or manually do something to propagate it? I think the failure of checked exceptions in Java to be a really interesting example to study for language designers, because on the surface, it seems like it was mostly a good idea. Yet in practice it was nearly universally disliked.
- estebank 7y agoWhen in Java you add a method call with an exception you either handle it or add the method call and the exception type to the list. In Rust when you add a method call that has a Result, you either handle it or add ? to the end of it to propagate the error, auto converting to the appropriate error type. The experience is much better than on Java, while still properly documenting where failures can happen and what isn't being handled.
- rhinoceraptor 7y agoIt seems like Go's error handling is basically like node's err callback argument, maybe a little cleaner. But it's still not that strict in forcing you to deal with errors. You could just rename err to _ and just drive on. In Rust, you either have to deal with the error, or explicitly ignore them. And if the error is the type that warrants crashing, that's easy too.
- anuragsoni 7y agoWhile its true that one can use `unwrap`, pattern matching and other higher order methods available for the Result type are a more idiomatic way to work with these. For example: match <something that returns result> { Ok (v) => <do something with v>, Err (some error) => <handle error> } By using the result type, the compiler ensures that the only way to get a value out of the result type is via pattern matching. So the errors are still values, except there is no need to come up with a default value for the error scenario, and there is no need to come up with an "empty" value. In isolation it might look verbose, but rust also provides a lot of higher order operations that one can do on result types. Ex: myResult.map(<do-something>).and_then(sq).or_else(<do-something-else>); Something else that I personally like about a result type is that it also serves as documentation by clearly marking operations that can fail, and have a consistent way for the end-user to know which value indicates that the response is a success or an error. EDIT: Formatting
- dnautics 7y agoIf you'd like to see something that's not "if err != nil", I recommend looking into elixir; there is the option of doing ok/error tuples, and there is a more ergonomic railway programming structure called "with". However, as it is a BEAM language, often the best solution is to "let it fail" as an error handling strategy. Let the process raise and crash, and a supervisor will restart it in a safe state if necessary.
- sanderjd 7y agoIn Go, you don't have to check err != nil to get the underlying value, in Rust you do. You probably aren't missing that exactly, in that you probably knew that difference, but you may be missing its importance. It means that in Rust, it is diminishingly rare to have a value of some type which it is not valid to use, whereas in Go many or most values - any value returned from a function that also returns an error, which is a lot of them - may be invalid (because the error may not have been checked). In mature teams using Go it may be unlikely for any errors to be unchecked, but it is a gun that is always pointed at your foot.