There is a special statement in the Go language. The defer statement will defer the statements that follow it. When the function that defer belongs to is about to return, the deferred statement will be executed in reverse order of what deferred is. The statement that was deferred last is executed first.

The use of the keyword defer is similar to the finally statement block in the object-oriented programming language Java, which is typically used to release some allocated resource, typically by unlocking a mutex or closing a file.

1. Multiple delayed execution statements

When there are multiple defer statements, they are executed in reverse order (last in, first out).

For example, the following code delays processing a sequence of numeric print statements:

package main

import "fmt"

func main(a) {
	fmt.Println("defer begin")

	defer fmt.Println(1)

	defer fmt.Println(2)

	defer fmt.Println(3)

	fmt.Println("defer end")}Copy the code

The following output is displayed:

defer begin
defer end
3        
2        
1        

Copy the code

The results are analyzed as follows:

  • The delay order of the code is reversed from the final execution order.
  • Deferred calls are made indeferAt the end of the function, which can be a normal return or an outage.

Use deferred execution statements to release resources when a function exits

Dealing with paired operations involved in business or logic, such as opening and closing files, receiving and replying to requests, locking and unlocking, can be tedious. The most overlooked of these operations is to release and close resources correctly at each function exit.

The defer statement happens to be the one executed when the function exits, so using defer is a very convenient way to handle resource release issues.

2.1 Use delay and unlock

In the following example, map is used concurrently in the function. To prevent race problems, sync.Mutex is used to lock, as shown in the following code:

var (
    // A demo mapping
    valueByKey      = make(map[string]int)
    // mutex to ensure concurrency safety when using mappings
    valueByKeyGuard sync.Mutex
)

// Read values by key
func readValue(key string) int {
    // Lock the shared resource
    valueByKeyGuard.Lock()
    / / value
    v := valueByKey[key]
    // Unlock the shared resource
    valueByKeyGuard.Unlock()
    / / the return value
    return v
}
Copy the code

Simplify the above statement with the defer statement:

func readValue(key string) int {
    valueByKeyGuard.Lock()
    // The statements following defer will not be called immediately, but deferred until the end of the function
    defer valueByKeyGuard.Unlock()
    return valueByKey[key]
}
Copy the code

2.2 Use deferred release file handles

You need to open the file, obtain and operate the file resource, and close the file resource. If the file resource is not closed after the operation, the file resource cannot be released.

Example below will be implemented according to the function of the file name for the file size, function need to open the file, the file size and close the file operations, such as every step system operation due to the need for error handling, and each step processing can create a possible exit, so you need to release resources at the exit, Instead, we need to pay close attention to releasing file resources correctly at function exit, as shown in the following code:

// Query the size according to the file name
func fileSize(filename string) int64 {
	// Open the file according to the file name, return the file handle and error
	f, err := os.Open(filename)
	// If an error occurs while opening, return file size 0
	iferr ! =nil {
		return 0
	}
	// Get file status information
	info, err := f.Stat()

	// If an error occurs while retrieving information, close the file and return the file size as 0
	iferr ! =nil {
		f.Close()
		return 0
	}
	// Take the file size
	size := info.Size()
	// Close the file
	f.Close()

	// Returns the file size
	return size
}
Copy the code

In the example above, f.close () is the file close operation, and we simplify the code by using defer, which looks like this:

func fileSize(filename string) int64 {
	f, err := os.Open(filename)
	// Delay calling Close, in which case Close will not be called
	defer f.Close()
	iferr ! =nil {
		return 0
	}

	info, err := f.Stat()
	iferr ! =nil {
		// defer is triggered by calling Close to Close the file
		return 0
	}
	size := info.Size()
	// defer is triggered by calling Close to Close the file
	return size
}
Copy the code