• Errors are returned as the last value of a function
  • Return nil if no error occurred
  • Errors are simply an interface that implements the Error() string function
  • You can create your own error types by implementing a type and creating a receiver function Error() string
// error implementation
 
type MyErrorType struct {
	a, b int
}
 
// always implement Error() functions with a pointer receiver!
func (m *MyErrorType) Error() string {
	return fmt.Sprintf("I found an error with %v and %v.\n", m.a, m.b)
}
 
// usage of your error
func ReturnError() (int, error) {
	if thereWasAnError {
		return 0, &MyErrorType{1, 2} // ALWAYS return a pointer to the error struct
	}
	if simpleError {
		// create a new error on the fly
		return 0, errors.New("Error string goes here") 
	}
}
 
// checking error type
func CheckErrorType() {
	_, err := someFunc()
	if err != nil {
		var SpecificError = MyErrorType{0, 1}
		if errors.Is(err, &SpecificError) {
			// the returned error is a MyErrorType with values 0, 1
		}
	}
	
	// errors.As(err, **) takes in a pointer to a pointer to an errorType and converts the err information into that type if possible
	_, err := someFunc()
	if err != nil {
		var thisError *ErrorType
		if errors.As(err, &thisError) {
			// thisError now contains all the information from the err
		} else {
			// err could not be converted to ErrorType
		}
	}
}