In Go, the default error type is an interface that can only contain a string. However, for more structured error handling, you can create custom error objects that include additional information like status codes or nested errors.
The Error Interface
Go’s error is an interface type:
type error interface {
Error() string
}
Any type that implements the Error() string method satisfies the error interface. The fmt package calls this method to print errors.
Implementing a String() Method
You can create a custom error type with a String() method for basic formatting:
package main
import (
"errors"
"fmt"
)
// CusErr is a custom error object
type CusErr struct {
Status int
Err error
}
func (cusErr *CusErr) String() string {
return fmt.Sprintf("%s ,%d", cusErr.Err, cusErr.Status)
}
func main() {
cus := &CusErr{10, errors.New("abc error")}
fmt.Println(cus)
cus2 := testErr()
fmt.Println(cus2)
}
// testErr returns a custom error
func testErr() *CusErr {
return &CusErr{1, errors.New("test error")}
}
Output:
abc error ,10
test error ,1
Implementing the Error() Method to Satisfy the Error Interface
To make your custom type satisfy the error interface, implement the Error() method:
package main
import (
"errors"
"fmt"
)
// CusErr is a custom error object
type CusErr struct {
Status int
Err error
}
func (cusErr *CusErr) Error() string {
return fmt.Sprintf("msg: %s ,code: %d", cusErr.Err, cusErr.Status)
}
func main() {
cus := &CusErr{10, errors.New("abc error")}
fmt.Println(cus)
cus1 := testErr1()
fmt.Println(cus1)
cus2 := testErr2()
fmt.Println(cus2)
if cuserr, ok := cus2.(*CusErr); ok {
fmt.Println("status:", cuserr.Status)
}
}
func testErr1() *CusErr {
return &CusErr{1, errors.New("test error")}
}
func testErr2() error {
return &CusErr{2, errors.New("test error")}
}
Output:
msg: abc error ,code: 10
msg: test error ,code: 1
msg: test error ,code: 2
status: 2
Best Practices
- Type Assertion: Use type assertion to check and access custom error fields, as shown in the example.
- Wrapping Errors: Go 1.13 introduced
fmt.Errorfwith%wfor wrapping errors, allowingerrors.Isanderrors.Asfor better error handling. - Avoid Over-Engineering: Only create custom errors when you need additional context beyond a simple string.
Conclusion
Custom error objects allow for richer error information and better debugging. By implementing the Error() method, your types can seamlessly integrate with Go’s error handling ecosystem.