Hacker News new | past | comments | ask | show | jobs | submit login

Go doesn't really warn you if you forget to handle an error return, which in my experience continuing silently in the face of error conditions is far scarier than crashing loudly.



Check out ErrCheck. It's a static analysis tool that detects exactly this. We added it to our common Makefile that we use for testing/building Go projects. Typically I have it test for this among other things before I commit, and then it runs again in CI scripts before a merge to main is allowed.

In my experience, if you're not checking errors, then you're often just going to crash loudly. Likely at a similar point that the comparable Python would have had its runtime error. In my experience, if Go would have continued silently, the comparable Python might also just continue silently anyway.


The problem with error checking is that Go doesn't cover every case BY FAR.

EVERY memory allocation can fail. And I mean EVERY.

    var x := 5 // where's the error handling?
EVERY kernel call can fail. Even this is still not a 100% correct way to call fmt.Printf("Hello, World!"):

    s := "Hello, World!")
    writtenSoFar := 0
    while writtenSoFar < len(s) {
      bytesWritten, err := fmt.Print(s[writtenSoFar:]) // and even this is a recent syntax addition.
    if errno, ok := err.(syscall.Errno); ret == -1 && ok {
      // error signaled
      if errno == C.EAGAIN {
        time.Sleep()
        continue
      } else {
        return err
      }
      writtenSoFar += bytesWritten
    }
If you don't do this, you will find, for example, that writing large amounts of data to a network socket suddenly only sends half the output to the other side. Plus anything could set O_NONBLOCK on stdout, which would require this. And time.Sleep() is required in some cases where the program redirects os.Stdout to itself.

Even this does not take have proper reactions if an OOM occurs somewhere. So it is still not correct.

It's like C. Simple Go looks correct and just chugs along, destroying data instead of crashing. This makes people feel programs run correctly ... but they don't.


In practice, how often does your rant on memory safety really apply though? Because I currently feel that you're inflating the argument substantially to make it seem like a much bigger, much more common problem, than it is. For reference, in 5-6 years of Go programming, memory allocation has been a problem for me exactly one time, and it was because I was a noob and tried to push about 60GB of data into a variable at once on a virtual machine with 32GB of memory available to it. And it wasn't a silent error, the systemd service I wrote for the application was crashing each time it attempted to load up that mega-variable until I rewrote it in a sane way.


Well that's the problem with correctness. I've seen this fail, in 20 years (that I noticed) about 20x. Let's assume I caught it 1% of the times I actually saw it so ... about once a week. More on slower networks.

This issue, not checking the number of bytes written, usually combined with incorrect EAGAIN handling, is a pretty pervasive problem in network programming. You will find the closer you get to 100% cpu usage, the more common this problem becomes, just like threading bugs. It's one of the ways a service goes from handling 5 Gbit at 90% cpu usage, then handling 5 kbps at 95% cpu usage (because everything suddenly errors out, then retries eat all the bandwidth). It's impossible to find if you don't know what you're looking for.

It's not this issue specifically: Golang programs, like C programs, are strongly incentivized to just keep going with incorrect data when other languages would crash.


That would not be a compile error, but these sorts of issues are why you should also run `go vet` and `golint` for a healthy codebase




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

Search: