进行错误分组的最佳方法是什么?

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"
}

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.