2

I was looking at net/http and crypto/x509

I wondering which approach is better and why.

net/http/http.go uses strings:

// HTTP request parsing errors.
type ProtocolError struct {
  ErrorString string
}

func (err *ProtocolError) Error() string { return err.ErrorString }

var (
  ErrHeaderTooLong        = &ProtocolError{"header too long"}
  ErrShortBody            = &ProtocolError{"entity body too short"}
  ErrNotSupported         = &ProtocolError{"feature not supported"}
  ErrUnexpectedTrailer    = &ProtocolError{"trailer header without chunked transfer encoding"}
  ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"}
  ErrNotMultipart         = &ProtocolError{"request Content-Type isn't multipart/form-data"}
  ErrMissingBoundary      = &ProtocolError{"no multipart boundary param in Content-Type"}
)

crypto/x509/verify.go uses ints:

type InvalidReason int

const (
  TooManyIntermediates
  IncompatibleUsage
)

type CertificateInvalidError struct {
  Cert   *Certificate
  Reason InvalidReason
}

func (e CertificateInvalidError) Error() string {
  switch e.Reason {
  case TooManyIntermediates:
    return "x509: too many intermediates for path length constraint"
  case IncompatibleUsage:
    return "x509: certificate specifies an incompatible key usage"
  }
  return "x509: unknown error"
}
0

1 Answer 1

1

Both usage are good, but it depends on your needs.

If you find it useful to attach additional data to the error that doesn't show in the error message, then the approach in crypto/x509 is preferable.

But I think in most cases, the simple error string as found in the errors package is sufficient.

Edit

An error can have different "attributes":

Describing
The Error() method should return a short describing error message

Identifiable
By letting a package export the different errors it might return, you can identify them. This is either done like in the io package by exporting initialized error variables of same type:

if err == io.EOF { ... } // That's easy

Or like in the encoding/json package by exporting the different error types:

if mErr, ok := err.(*json.MarshalerError); ok { ... } // That's fairly easy

Or by doing like they do in the crypto/x509 package, by exporting the different Reasons (error codes):

if e, ok := err.(x509.CertificateInvalidError); ok && e.Reason == x509.Expired  { ... } // Well, it works

Unique error code
If errors should have specific codes due to a protocol spec, these could be embedded in the error variable. The crypto/x509 package might be used for that, even though it is probably not the case.

But when it comes to how to solve it, I think there is no best approach, nor any clearly idiomatic one. The examples above shows you ways to do it and ways it is done in the standard libraries. Take your pick.

.. but maybe not using switch statements.

Sign up to request clarification or add additional context in comments.

4 Comments

I don't see the advantage of using ints (Reason InvalidReason) and then going through a switch statement.
You've got a point there. I was more focused on the struct. No, honestly I would agree that the switch statement is a bit peculiar. One reason could be wanting to have some kind of int code for each error, eg. easy to transfer over a network. But I doubt that is the reason. And even in such a case, there are better solutions.
Interesting, it does make sense if the error code is part of a protocol.
I extended the answer a bit. But still, the answer 'it depends' remains.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.