SoFunction
Updated on 2025-03-03

Comprehensive analysis of Go language standard error error

Error Type

  • errorString

Errors are an important part of the new logic and system stability in programs.

Built-in errors in go language are as follows:

// The error built-in interface type is the conventional interface for// representing an error condition, with the nil value representing no error interface {    Error() string}

errorType is an interface type, including oneErrormethod. It is the wrong top-level interface, and the structures that implement this built-in method are all subclasses of it.

errorStringThe structure is a built-in implementation of the error interface built-in, the source code is as follows:

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
	return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return 
}

NewThe method is built-in error type implementation.

() is the most commonly used error class implementation method.

  • wrapError

wrapErroris another implementation class of error, located infmtThe source code in the package is as follows:

// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// If the format specifier includes a %w verb with an error operand,
// the returned error will implement an Unwrap method returning the operand.
// If there is more than one %w verb, the returned error will implement an
// Unwrap method returning a []error containing all the %w operands in the
// order they appear in the arguments.
// It is invalid to supply the %w verb with an operand that does not implement
// the error interface. The %w verb is otherwise a synonym for %v.
func Errorf(format string, a ...any) error {
	p := newPrinter()
	 = true
	(format, a)
	s := string()
	var err error
	switch len() {
	case 0:
		err = (s)
	case 1:
		w := &wrapError{msg: s}
		, _ = a[[0]].(error)
		err = w
	default:
		if  {
			()
		}
		var errs []error
		for i, argNum := range  {
			if i > 0 && [i-1] == argNum {
				continue
			}
			if e, ok := a[argNum].(error); ok {
				errs = append(errs, e)
			}
		}
		err = &wrapErrors{s, errs}
	}
	()
	return err
}

type wrapError struct {
	msg string
	err error
}

func (e *wrapError) Error() string {
	return 
}

func (e *wrapError) Unwrap() error {
	return 
}

wrapErroris another built-in error implementation class, using%wAs a placeholder, wrapError here is implementedUnwrapMethod, the user returns the built-in err, that is, the nested err.

wrapError also has a plural formwrapErrorsI won't go into details here.

  • Custom errors

Implementing custom errors is very simple. Implementing the error interface erros with object-oriented features is to implement the error class. Install the inherited features of the Go language and implement the corresponding methods of the interface.

type error interface {
	Error() string
}
type MyErr struct {
	e string
}

func (s *MyErr) Error() string {
	return 
}


func main(){
	var testErr error
	testErr = &MyErr{"err"}
	(())	
}

The above code implements a custom error type. Note that it is a structure, but it is actuallyerrorStringsubclass of .

New error

The previous section introduces three error types. The first two are built-in error types, among which the customized errors are expandable and can implement any of the first two.

The first typeerrorStringIt is relatively convenient to implement, onlyError()method;

The second type iswrapErrorTwo methods are needed, and one isUnwrap()

  • ()
err := ("this is a error")
("----%T----%v\n", err, err)

This method createserrorStringClass instance

  • ()
err = ("err is: %v", "no found")
(err)

This method createswrapErrorClass instance, wrapError is also a subclass of errorString.

  • Instantiation
type MyErr struct {
	e string
}

func (s *MyErr) Error() string {
	return 
}


func main(){
	var testErr error
	testErr = &MyErr{"err"}
	(())
}

Since custom errors generally require error information, the instantiation of the method is generally constructed directly.

Error parsing

  • ()

Used to determine whether one error is equal to another specific error. It not only simply compares the wrong values, it also checks all errors in the error chain to see if they match the given target error.

package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = ("not found")

func findItem(id int) error {
    if id == 0 {
        return ErrNotFound
    }
    return nil
}

func main() {
    err := findItem(0)
    if (err, ErrNotFound) {
        ("Item not found")
    } else {
        ("Item found")
    }
}

Note that there is a pit here, which is the type of error and error information judged by the Is method. Using the New method, even if the error information is the same and different, it is not equal, as follows:

err1 := ("err1")
err2 := ("err1")
err := (err1, err2)
(err) // Output: false
  • ()

Used to convert an error to a specific error type. If an error in the error chain matches the given target type, the error is converted to that type and assigned to the target variable.

package main

import (
    "errors"
    "fmt"
)

type MyError struct {
    Code int
    Msg  string
}

func (e *MyError) Error() string {
    return ("code %d: %s", , )
}

func doSomething() error {
    return &MyError{Code: 404, Msg: "Not Found"}
}

func main() {
    err := doSomething()
    var myErr *MyError
    if (err, &myErr) {
        ("Custom error: %v (Code: %d)\n", , )
    } else {
        ("Unknown error")
    }
}

Can be used as type judgment.

  • ()

()is a function for handling nested or wrapped errors. Its main purpose is to extract and return an error direct underlying error (i.e., a wrapped error). If the error has not been wrapped, it will returnnil

package main

import (
	"errors"
	"fmt"
)

func main() {
	baseErr := ("base error")
	wrappedErr := ("wrapped error: %w", baseErr)

	// Use to extract the underlying error	unwrappedErr := (wrappedErr)
	("Wrapped error:", wrappedErr)
	("Unwrapped error:", unwrappedErr)
}


// Output
Wrapped error: wrapped error: base error
Unwrapped error: base error

This method can only obtain direct embedded errors for errors, and it is necessary to make easy judgments if you want to obtain deeper errors.

Notice:

  • : Used to determine whether an error is equal to a specific error value. Suitable for finding a specific error in the error chain (such as a known predefined error).
  • : Used to convert errors to specific error types. Suitable for use when you need to handle errors based on the specific type of error.
  • ()Used to extract from a wrapper error and return the underlying error. If the error has not been wrapped (or the Unwrap method is not implemented), it will returnnil

Error handling

if err := findAll(); err != nil {
	// logic
}

Is this process very familiar?Error parsingThe processing method of subsections can perform subsequent processing of error judgments.

Go language also provides error capturerecoverMechanism and error throwingpanicmechanism.

  • panic

panicIt is a way to trigger exception handling mechanism in Go language. When you call panic, the program will immediately stop executing the current function and throw it up layer by layer from the call stack until a suitable recovery is found, or it will eventually cause the program to crash.

package main

import "fmt"

func main() {
    ("Start")
    panic("Something went wrong!")
    ("End") // This line will not be executed
}

When called in the program panic, the program will immediately abort the current control flow and start backtrackingStackframes and execute each layer ofdefer Statement. After executing all defer statements, if norecover, the program will crash and print panic information and stack trace.

  • recover

recover is a built-in function to restore panic. It can only bedeferValid in the function. When panic occurs, ifCurrent functionorAny call stackThe defer function in the function on it calls recovery, so the content of panic can be captured and the program can be restored to normal execution.

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            ("Recovered from:", r)
        }
    }()
    ("Start")
    panic("Something went wrong!")
    ("End") // This line will not be executed
}

Recover is usually used to ensure that some blocks of code can perform resource cleanup operations even if panic occurs and avoid the entire program crash.

  • defer

deferStatements are used to delay the execution of functions and will not be executed until the function containing the defer statement has been executed. This is usually used to ensure that resource release or cleaning operations (such as closing files, unlocking mutexes, etc.) are performed even when an error occurs in the function or returns early.

  • Execution order: In a function, you can have multiple defer statements. The order of execution of these defer calls is the last-in-first-out (LIFO). That is, the last defer statement will be executed first.
  • Capture valueThe defer statement captures the value of the variable it references when declared. That is, the parameters in the defer statement are calculated when defer is declared, not when defer is executed.

The combination of three functions constitutes an error handling, as follows:

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            ("Recovered from panic:", r)
        }
    }()

    ("Starting the program")
    causePanic()
    ("Program ended gracefully")
}

func causePanic() {
    ("About to cause panic")
    panic("A severe error occurred")
    ("This line will not execute")
}
Starting the program
About to cause panic
Recovered from panic: A severe error occurred

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.