4 ms·
The difference is that all code paths are explicitly spelled out and crucially that the programmer had to consider each path at the time of writing the code. Th
by Mawr 2y ago
The difference is that all code paths are explicitly spelled out and crucially that the programmer had to consider each path at the time of writing the code. The resulting code is much more reliable than what you end up with exceptions.
- bootsmann 2y agoDo you really do that in practice, or do you just blindly go 'if err != nil return nil, err'? Because fundamentally the function you called can return different errors at any point so if you just propagate the error the code paths are in fact not spelled out at all because the function one above in the hierarchy has to deal with all the possible errors two calls down which are not transparent at all.
- throwaway894345 2y agoIn Go, no one really blindly returns nil, err. People very clearly think about errors—if an error may need to be actioned on up the stack, people will either create a named error value (e.g., `ErrInvalidInput = errors.New(“invalid input”)` or a named error type that downstream users can check against. Moreover, even when propagating errors many programmers will attach error context: `return nil, fmt.Errorf(“searching for the flux capacitor `%s`: %w”, fluxCap, err)`. I think there’s room for improvement, but Go error handling (and Rust error handling for that matter) seem to be eminently thoughtful.
- 4star3star 2y agoComing from dotnet, I rather like the Go pattern as you've described it. I would normally catch and error and then write out a custom message with relevant information, anyway, and I hate the ergonomics of the try{}catch(Exception ex){} syntax. And yes, it is tempting to let the try block encompass more code than it really should.
- throwaway894345 2y agoYeah, I was pretty skeptical when they added the error wrapping stuff to the standard library, and it still feels a little too squishy, but in practice it works very well. I prefer Go’s error handling even to Rust’s much more explicit error handling.
- Mawr 2y agoI don't see how it's possible to do it blindly unless the code gets autogenerated. If you're typing the `if err != nil` then you've clearly understood that an error path is there. There's no requirement for the calling function to handle each possible type of error of the callee. It can, as long as the callee properly wrapped the error, but it's relatively rare for that to be required. Usually the exact error is not important, just that there was one, so it gets handled generically.
- Capricorn2481 2y agoTheir point was writing `if err != nil return nil, err` is the same thing that stack traces from exceptions do, but with even less information. And if that's most of a Golang codebases error handling, it's not a compelling argument against exceptions.
- throwaway2037 2y agoI understand your sentiment. The debate of error codes vs exceptions will be debated until the year 3000, and further. One point to consider with exceptions: It is "impossible" to ignore an exception. The function implementation is telling (nay: dictating[!] to) the caller: You cannot ignore this error code. At the very least, you must catch, then discard. Another point this is overlooked in these discussions: Exceptions and error codes can, and do, peacefully co-exist. Look at Python, C#, and Java. In the standard library for all three, there are cases where error codes are used and cases where exceptions are thrown. Another thing about exceptions, especially in enterprise programming, you can add a human readable error message. That is not possible when only returning error codes. EDIT Inspired by this comment: https://news.ycombinator.com/item?id=40220147 https://news.ycombinator.com/item?id=40220147 I forgot about exception stack traces, including "chained" exceptions. These are incredibly powerful when writing enterprise software that commonly has a stack 50+ levels deep.
- Mawr 2y ago> One point to consider with exceptions: It is "impossible" to ignore an exception. The function implementation is telling (nay: dictating[!] to) the caller: You cannot ignore this error code. At the very least, you must catch, then discard. Error returns are no different, assuming a proper implementation like the Result type in Rust. The difference is, unhandled error returns are found at compile time but unhandled exceptions only show up at runtime, when it's too late. > Another point this is overlooked in these discussions: Exceptions and error codes can, and do, peacefully co-exist. Both Go and Rust have panics, which are basically exceptions that are generally not supposed to be caught. They are used for unrecoverable cases like running out of memory or programmer mistakes. There's otherwise no reason to mix the two. > Another thing about exceptions, especially in enterprise programming, you can add a human readable error message. That is not possible when only returning error codes. I don't really know what you mean, it's equally possible in both cases. If anything, the error return implementation that Go uses is probably the most optimal out there when it comes to error messages. Most look like: return nil, fmt.Errorf("opening file %s as user %s: %w", file, user, err) Whereas most exception code will just dump a stacktrace since that's the default.