Hacker Timesnew | past | comments | ask | show | jobs | submitlogin

It's not "tempting to ignore errors". You're the programmer, if you want to ignore it, do so. I don't know why you'd want to ignore the returned value and possible error on a GET request - given that 1) it can fail for so many reasons, and 2) the suggestion that you might be "GETTING" something. Ignoring the error of a print statement is more acceptable since it's unlikely to occur and there's rarely a lot you can do about it anyway.

Error handling is explicit, like the quote you quoted said it should be. If you ignore the returns (either explicitly or implicitly) you pay the price in debugging.

"leaving me confused as to which error happens when" Panics happen when something very bad happens. You shouldn't be accessing off the end of an array, it signals that you've probably programmed it incorrectly, rather than it being a problem with your input (~ish). Same with divide-by-zero and type assertions. However, they provide another control path that in a limited number of situations, can give elegant results.



That's his whole point. He doesn't want to ignore errors but feels like Go is so verbose about it that it's a pain for programmers to deal with it. Example in case, print. Would you check its return value every time you call it? No.

Contrast that to other languages, such as python, where you don't ignore the errors because you know that if something goes wrong, an exception will be raised.

It lets you do stuff like:

def whatever(request): my_int = int(request.POST['some-param'])

Note that, in that case, we don't check if 'some-param' is in POST. Neither what happens if it's not a number.. Basically, an exception will be raised and a 404 will be shown.


> Basically, an exception will be raised and a 404 will be shown.

I understand your point in general, but have to disagree with the example you chose. A 404 implies Page Not Found.

The Python framework I use (http://bottlepy.org/docs/0.9/_modules/bottle.html) makes this a 500 Internal Server Error, with the text "Critical error while processing request: /foo-bar". IMO, this is ugly, and not super user-friendly, so I usually end up writing try catches around "one-liners" to log a better error than a stack dump, and present a friendlier message.

If he really wants this kind of framework-provided error message, then a panic would be more appropriate, because the HTTP server in Go will do the same thing as bottle. It's definitely not going to be as succinct, but it'll be more succinct.


It's actually worse than that. The blog has error handling code like:

    if err := datastore.Get(c, key, record); err != nil {
        return &appError{err, "Record not found", 404}
    }
This is pretty typical Google Go error handling, where errors are only actually used as boolean flags. Errors are universally returned as type 'error' so to actually do anything with the error you have to first cast it to some specific type. But that type isn't part of the method signature so you have to dig around in the source or, if you are very lucky, it was documented someplace. You end up having actual error handling plus even more error handling for if you got the error type wrong.

For instance if datastore could return 'record not found' or 'io error' then they would usually be treated the same way by the code because to do otherwise would be even that more of a hassle.


I guess you are super lucky: https://developers.google.com/appengine/docs/go/datastore/re... or even https://developers.google.com/appengine/docs/go/datastore/re...

  err := datastore.Get(c, key, record)
  if err == ErrNoSuchEntity {
      // entity not found
      return
  } else if err != nil {
      // some other error
      return
  }
Don't really see the hassle you are referring to.


I think this is exactly the point. You have to read the comment to get the error types (isn't compiler accessible), it doesn't list what all the possible error types are (doesn't say the ones documented are exhaustive), and the code returns a singleton error (no state) presumably so the caller doesn't have to do a bunch of casts..


> You have to read the comment to get the error types (isn't compiler accessible), it doesn't list what all the possible error types are (doesn't say the ones documented are exhaustive)

You're absolutely right. People should be able to use libraries without reading their documentation, and instead rely on compile failures to slowly iterate their code towards perfection. /s

Seriously, though, I believe expecting programmers to read documentation of a library they're using is a pretty low bar.

Also, no it doesn't list what all the possible error types are. To do this would require what amounts to checked exceptions in Java. The issues with these are relatively well known.

First, it complicates versioning as adding a new error type is a breaking change for all clients. When they get an error from an API, most clients either return that error verbatim, decorate that error slightly and return the decorated error, or handle a few specific error situations and return an error in all other cases. Declaring all possible error types makes your programs brittle, as it is easy for libraries you are using to break your code.

Second, they are a hassle for larger programs that touch many systems. It is easy to declare that you return an EntityNotFound error. But it is not so easy to declare that you return an EntityNotFound, MemcacheCASConflict, FileNotFound, FileExists, PermissionDenied, Timeout, InvalidJSON, ConnectionClosed, or TemplateRenderingFailed error. This is perfectly reasonable set of errors for a simple method that gets a value from datastore (possible caching it) decoding a field as json, and writing a template to an HTTP connection. Any 'simple' wrapper of this method then inherits all of these declarations. Now certainly with Java, IDEs will "fill in all the forms" for you, so this problem is a little more palatable, but Go does not require programmers to use (often heavyweight) tools to make them productive.

> code returns a singleton error (no state) presumably so the caller doesn't have to do a bunch of casts

This is a little disingenuous, as you don't really know why they made it a singleton error. I can't think of any state that would be useful in this situation, can you?

The Java version of this API throws an exception with a single field, the Key that could not be found. I think this is supremely unhelpful and just adds clutter to the documentation, as the user of this method clearly already has this information in hand.


You can switch on the error type, since "error" is an interface.

    switch err.(type) {
       case Type1: 
          // do something
       case Type2:
          // something different
       default: 
          // something else
    }


> Errors are universally returned as type 'error' so to actually do anything with the error you have to first cast it to some specific type. But that type isn't part of the method signature so [...]

This is quite correct and what's really broken with Go's approach to error handling and documentation.


I respectfully disagree. You almost never cast the error. Two approaches are common in the go community.

1. Error constants. These you can use == or a switch statement to do control flow with.

2. Custom Error Types. These you typically use a type switch which uses reflection to do dispatch off. In this case you might also then cast it if you need specific data off of the error but most of the time you don't need any more than the string the error() method returns.

Both of these cases use errors in the way go intends them to be used. For control flow at the location where it makes the most sense.


I respectfully disagree: http://golang.org/src/pkg/net/timeout_test.go#L39

edit:

You should note that the above test code is actually far less verbose than the analogous end-user code, given that the timeout_test.go clearly expects the 'err' var to be a net.Error.

Now consider the case of a network subsystem, with funcs having in args of type 'bufio.Reader' and/or 'bufio.Writer'. At some prior point you may have set 'net.Conn.SetReadDeadline(aPointInTime)', and in your stream processing funcs you may naturally encounter either net.Error (due to timeout) or buffer overruns, etc.

What would call site look like? Can you safely cast to (net.Error) like the test code? Not unless you can live with panics on interface type mismatch.

   // hmm. what is the error type?
   _, e := readMessage(reader) 
   switch errType(e) { // you write this reflective func
   case NET_ERROR:
      if e.(net.Error).timeout() { /* handle timeout */}
   default:
         /* deal with other errors */
   }


You are using an internal testing package as an example?

First you wouldn't cast to net.Error like the test code for real code. That code is testing internal details of the net package and not meant to be a guide for idiomatic consumption of the code at the level you are describing.

here is what you would actually do in production code:

    _, e := readMessage(reader)

    switch et := e.(type) {
    case net.OpError:
       // handle OpErrors specifically
      if et.timeout() { /* handle timeout */ }
    case net.AddrError:
      // handle AddrErrors specifically
    case net.DNSError:
      // handle DNS errors specifically
    case net.Error:
      // handle all the rest of the net packages errors
    default:
      // deal with other errors
    }
Of course that's just for low level code. if you want to see if net.Error has code leaking though the wrapping package then you would perhaps use a case looking for all the wrapper packages specific errors and then look for net.Error types after that.

Most of the time you won't be dealing with the net package either. Instead you will be dealing with the http package for instance where your errors will mostly look like this: http://golang.org/pkg/net/http/#variables for which you can use a regular switch. I have just listed both of my cases taken directly from the stdlib including the case you listed.


Not sure what buffer overruns you are referring to, or why this case requires reflection at all.

  err := readMessage(reader)
  if n, ok := err.(net.Error); ok && n.Timeout() {
      // handle timeout
  } else if err != nil {
      // handle other errors
  }
You can see an example of exactly this pattern here: http://golang.org/src/pkg/net/http/server.go?s=29962:30008#L...


You did not carefully read the GP.

IO read/write functions are not limited to the net package. net.Conn supports a subset of these (interface types) and it is entirely idiomatic and possible for a net.Conn flavor to endup at some deep layer of your code in a function accepting generic io in args.

Functions taking non-net "in args" returning "error" can not be assumed to always return "net.Error". You will need to test the type (reflectively) and then safely cast.

For example : http://golang.org/pkg/io/#ReadFull

Is it "non-idiomatic" to wrap net.Conn as another non-net package type and then use it?

http://golang.org/src/pkg/net/net.go#L22


To be clear, the two-valued cast I used in my example never panics. It returns (casted value, true) or (nil, false). No need to write any reflective functions.

As to your example of io.ReadFull: what about it? If it gets an EOF before filling the buffer you passed in, it'll return ErrUnexpectedEOF per the documentation. If it gets any other error, it'll return it instead. I haven't actually looked at the source in a while but that's how almost all of the byte stream functions work.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

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

Search: