This has generally been my complaint with Go. Go is... fine. It offers a lot of features that seem revolutionary if you've never seen them before, but mostly feel like someone took the best parts of every language, and then implemented the least interesting aspects of those features.
- Interfaces are like typeclasses, but without actually offering any sort of higher-kinded polymorphism.
- Goroutines are kind of like Erlang processes, but without any notion of supervision and with shared mutable state.
- The error handling feels like it's trying to emulate an Either monad, but in a way that doesn't actually allow for the compositional properties that make an Either monad useful.
I know that Go's goal was never to innovate on language design, but I feel like it ignored a lot of low-hanging fruit in modern language design.
Obviously you can do worse than coding in Go, but for everything Go is good at, there's likely an even better tool for the job. I'm not convinced that there's any domain where Go is the best choice available, but it's definitely average at most jobs, and maybe that's where it finds its niche.
Edited: I had missed the word "higher-kinded" in front of polymorphism. Thanks donatj!
Can you explain what you mean by this? I certainly use them in a manner I would consider polymorphic.
Go's interfaces provide structural typing, which offers polymorphism over values. Typeclasses in comparison, offer polymorphism over types themselves. The distinction is subtle, but it drastically limits the sorts of abstractions that you can write with Go. In Go's defence, this is a limitation that even functional languages like F# and Elm face too. This sort of polymorphism is what makes ideas like monads and functors so powerful in a language like Haskell, and what causes those same ideas to lose some of their lustre in a less expressive language.
Unfortunately it's really difficult to find good articles on higher-kinded types, since anything written about them already assumes a ton of knowledge about the subject. This StackOverflow thread has some good insight though: https://stackoverflow.com/questions/21170493/when-are-higher...
It's entirely likely that in the real world these sorts of limitations don't actually matter, but my general frustration with Go stems from the fact that it's so close to being so much better than it is, and it saddens me that we as an industry chose to be so excited by something that's a tiny step forward, when there exist so many other options that are entire leaps and bounds forward.
If it's the only type of error your method returns you don't even need to, just define your return type as your custom error type.
The problem with returning them is it's tedious and you end up with error checking boilerplate everywhere.
The problem with throwing them is they're easy to ignore, and also you never know whether or not a function is capable of throwing an error, or what types of errors it can throw.
In conclusion, error handling sucks no matter how you do it :-/