Examples of key features of defer in golang are detailed

  • 2020-06-07 04:40:38
  • OfStack

preface

The defer keyword of golang is well known for performing operations before a function returns, most commonly using defer to delay the closing of a resource (such as a file, database connection, and so on) to avoid memory leaks. This article mainly introduces the key features of defer and shares them for your reference and study. Let's start with a detailed introduction.

1. The role and timing of defer

The defer statement of go is used to delay the execution of a function, and the delay occurs after calling the function return, for example


func a() int {
 defer b()
 return 0
}

The execution of b occurs after return 0, note the syntax of defer, after the keyword defer is the function call.

2. Important use of defer 1: Cleaning up and releasing resources

Due to the delayed nature of defer, defer often cleans up associated resources after a function call, such as


f, _ := os.Open(filename)
defer f.Close()

The release of file resources is automatically done with defer at the end of the function call. There is no need to keep in mind where the resource needs to be released, and the open and release must correspond.

To illustrate the convenience and simplicity brought by defer in 1 example.

The main purpose of the code is to open 1 file and copy the contents to a new file.


func CopyFile(dstName, srcName string) (written int64, err error) {
 src, err := os.Open(srcName)
 if err != nil {
  return
 }
 dst, err := os.Create(dstName)
 if err != nil { //1
  return
 }
 written, err = io.Copy(dst, src)
 dst.Close()
 src.Close()
 return
}

After the code is returned at #1, the src file does not perform a close operation, which may cause the resource not to be released properly. Instead, implement defer:


func CopyFile(dstName, srcName string) (written int64, err error) {
 src, err := os.Open(srcName)
 if err != nil {
  return
 }
 defer src.Close()
 dst, err := os.Create(dstName)
 if err != nil {
  return
 }
 defer dst.Close()
 return io.Copy(dst, src)
}

Both src and dst can be cleaned up and released in a timely manner, no matter where return is executed.

Because of this role, defer is often used to free database connections, open file handles, and so on to free resources.

3. Important USES of defer 2: Implementing recover

The function being executed by defer comes after return, which is the point at which panic thrown can be caught, so another important use of defer is to execute recover.

recover makes more sense only if it is used in defer; if used elsewhere, program does not effectively catch errors because it returns early after the end of the call.


package main
import (
 "fmt"
)
func main() {
 defer func() {
  if ok := recover(); ok != nil {
   fmt.Println("recover")
  }
 }()
 panic("error")
}

Remember that defer precedes panic execution.

4. Execution sequence of multiple defer

The purpose of defer is to push the function execution after the key into one stack to delay the execution. The execution order of multiple defer is last in, first out.


defer func() { fmt.Println("1") }()
defer func() { fmt.Println("2") }()
defer func() { fmt.Println("3") }()

The output order is 321.

This feature allows you to reverse order an array.

5. The parameter of the deferred function is determined at defer

This is the characteristic of defer. When a function is evaluated by defer, its parameters are determined at defer. Even if the parameters are modified after defer, it has no effect on the function of defer. See the examples:


func a() {
 i := 0
 defer fmt.Println(i)
 i++
 return
}

The a execution outputs 0 instead of 1, because the value of i is 0 at defer, when the function argument of defer has been evaluated and determined.

Here's another example:


func calc(index string, a, b int) int {
 ret := a + b
 fmt.Println(index, a, b, ret)
 return ret
}
func main() {
 a := 1
 b := 2
 defer calc("1", a, calc("10", a, b))
 a = 0
 return
}

Execute code output


10 1 2 3 
1 1 3 4

The third parameter of the defer function is calculated and determined at the time of defer, as is the second parameter a, regardless of whether the a variable is modified later.

6. The defer function can read and modify named return values


func c() (i int) {
 defer func() { i++ }()
 return 1
}

The function defer is executed after return and can be modified to return a named value. The above function c returns 2.

conclusion

The resources

https://blog.golang.org/defer-panic-and-recover


Related articles: