PageRenderTime 41ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/_posts/2014-06-07-Why-Go-error-handling-doesnt-sit-right-with-me.md

https://github.com/SirCmpwn/sircmpwn.github.io
Markdown | 111 lines | 95 code | 16 blank | 0 comment | 0 complexity | 2825a4cb0af6c86ad8f5241b99e9f88c MD5 | raw file
  1. ---
  2. #vim: tw=82
  3. layout: post
  4. title: Why Go's error handling doesn't sit right with me
  5. ---
  6. I'll open up by saying that I am not a language designer, and I do like a lot of
  7. things about Go. I just recently figured out how to describe why Go's error
  8. handling mechanics don't sit right with me.
  9. If you aren't familiar with Go, here's an example of how Go programmers might do
  10. error handling:
  11. {% highlight go %}
  12. result, err := SomethingThatMightGoWrong()
  13. if err != nil {
  14. // Handle error
  15. }
  16. // Proceed
  17. {% endhighlight %}
  18. Let's extrapolate this:
  19. {% highlight go %}
  20. func MightFail() {
  21. result, err := doStuffA()
  22. if err != nil {
  23. // Error handling omitted
  24. }
  25. result, err = doStuffB()
  26. if err != nil {
  27. // Error handling omitted
  28. }
  29. result, err = doStuffC()
  30. if err != nil {
  31. // Error handling omitted
  32. }
  33. result, err = doStuffD()
  34. if err != nil {
  35. // Error handling omitted
  36. }
  37. }
  38. {% endhighlight %}
  39. Go has good intentions by removing exceptions. They add a lot of overhead and
  40. returning errors isn't a bad thing in general. However, I spend a lot of my time
  41. writing assembly. Assembly can use similar mechanics, but I'm spoiled by it (I
  42. know, spoiled by assembly?) and I can see how Go could have done better. In
  43. assembly, `goto` (or instructions like it) are the only means you have of
  44. branching. It's not like other languages where it's taboo - you pretty much *have*
  45. to use it. Most assembly also makes it fancy and conditional. For example:
  46. goto condition, label
  47. This would jump to `label` given that `condition` is met. Like Go, assembly
  48. generally doesn't have exceptions or anything similar. In my own personal flavor
  49. of assembly, I have my functions return error codes as well. Here's how it's
  50. different, though. Let's look at some code:
  51. {% highlight nasm %}
  52. call somethingThatMightFail
  53. jp nz, errorHandler
  54. call somethingThatMightFailB
  55. jp nz, errorHandler
  56. call somethingThatMightFailC
  57. jp nz, errorHandler
  58. call somethingThatMightFailD
  59. jp nz, errorHandler
  60. {% endhighlight %}
  61. The difference here is that all functions return errors in the same way - by
  62. resetting the Z flag. If that flag is set, we do a quick branch (the `jp`
  63. instruction is short for `jump`) to the error handler. It's not clear from looking
  64. at this snippet, but the error code is stored in the A register, which the
  65. `errorHandler` recognizes as an error code and shows an appropriate message for.
  66. We can have one error handler for an entire procedure, and it feels natural.
  67. In Go, you have to put an if statement here. Each error caught costs you three
  68. lines of code in the middle of your important logic flow. With languages that
  69. throw exceptions, you have all the logic in a readable procedure, and some error
  70. handling at the end of it all. With Go, you have to throw a bunch of
  71. 3-line-minimum error handlers all over the middle of your procedure.
  72. In my examples, you can still return errors like this, but you can do so with a
  73. lot less visual clutter. One line of error handling is better than 3 lines, if you
  74. ask me. Also, no one gives a damn how you format assembly code, so if you wanted
  75. to do something like this you'd be fine:
  76. {% highlight nasm %}
  77. call somethingThatMightFail
  78. jp nz, errorHandler
  79. call somethingThatMightFailB
  80. jp nz, errorHandler
  81. call somethingThatMightFailC
  82. jp nz, errorHandler
  83. call somethingThatMightFailD
  84. jp nz, errorHandler
  85. {% endhighlight %}
  86. Or something like this:
  87. {% highlight nasm %}
  88. call somethingThatMightFail \ jp nz, errorHandler
  89. call somethingThatMightFailB \ jp nz, errorHandler
  90. call somethingThatMightFailC \ jp nz, errorHandler
  91. call somethingThatMightFailD \ jp nz, errorHandler
  92. {% endhighlight %}
  93. The point is, I think Go's error handling stuff make your code harder to read and
  94. more tedious to write. The basic idea - return errors instead of throwing them -
  95. has good intentions. It's just that how they've done it isn't so great.