> For the complete documentation index, see [llms.txt](https://sunwenfei.gitbook.io/sunwenfei/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sunwenfei.gitbook.io/sunwenfei/golang/golang-ji-chu-jiao-cheng/defer.md).

# Defer

#### 什么是defer

`defer`翻译成中文是推迟的意思。`defer`语句用来推迟一个函数的执行，具体来说就是推迟到`defer`语句所在函数的最后。听起来有点晦涩难懂，我们通过一个例子来理解下。

```go
package main

import "fmt"

func finished() {
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {
    defer finished()
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
    }
    fmt.Println("Largest number in", nums, "is", max)
}

func main() {
    nums := []int{78, 109, 2, 563, 300}
    largest(nums)
}
```

这段程序比较简单，找到一个切片的最大值并打印。注意函数`largest`的第一行语句`defer finished()`，会推迟函数`finished()`的执行。`defer`所在的函数为`largest`，因此推迟到`largest`函数体的最后，即`return`之前。该程序输出如下：

```
Started finding largest
Largest number in [78 109 2 563 300] is 563
Finished finding largest
```

#### defer方法

前面的例子我们推迟了一个函数的执行。不过也可以推迟方法的执行。

```go
package main

import "fmt"

type person struct {
    firstName string
    lastName  string
}

func (p person) fullName() {
    fmt.Printf("%s %s", p.firstName, p.lastName)
}

func main() {
    p := person{
        firstName: "John",
        lastName:  "Smith",
    }
    defer p.fullName()
    fmt.Printf("Welcome ")
}
```

这里我们推迟了一个方法的执行`defer p.fullName()`。该程序比较简单，输出如下：

```
Welcome John Smith
```

#### defer函数/方法的参数

被`defer`执行的函数/方法的入参在`defer`语句执行时就已确定并计算好，而不是在函数/方法调用时。我们通过一个例子来理解下。

```go
package main

import "fmt"

func printA(a int) {
    fmt.Println("value of a in deferred function", a)
}
func main() {
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)
}
```

这里`a`的初始值为`5`，然后执行`defer`语句，将`a`值传递给被`defer`的函数`printA`，这时入参就已经确定了，即便后面我们又把`a`的值更新为`10`，`printA`执行时的入参仍然是`5`。该程序输出如下：

```
value of a before deferred function call 10
value of a in deferred function 5
```

#### defer栈

如果一个函数内同时出现了多个`defer`，那么这些`defer`是按照栈结构来执行的，即先`defer`的后执行，后`defer`的先执行的顺序。下面我们通过这个特性来将一个字符串反转下：

```go
package main

import "fmt"

func main() {
    name := "Naveen"
    fmt.Printf("Original String: %s\n", string(name))
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}
```

这里的`for range`循环遍历字符串的每个字符，执行`defer fmt.Printf("%c", v)`语句，由于被`defer`的函数是栈顺序，因此打印的字符串是反转过来的。该程序输出结果为：

```
Original String: Naveen
Reversed String: neevaN
```

#### defer真实适用场景

下面我们看下`defer`真正适用的场景。我们先不使用`defer`来实现下面的程序。

```go
package main

import (
    "fmt"
    "sync"
)

type rect struct {
    length int
    width  int
}

func (r rect) area(wg *sync.WaitGroup) {
    if r.length < 0 {
        fmt.Printf("rect %v's length should be greater than zero\n", r)
        wg.Done()
        return
    }
    if r.width < 0 {
        fmt.Printf("rect %v's width should be greater than zero\n", r)
        wg.Done()
        return
    }
    area := r.length * r.width
    fmt.Printf("rect %v's area %d\n", r, area)
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    r1 := rect{-67, 89}
    r2 := rect{5, -67}
    r3 := rect{8, 9}
    rects := []rect{r1, r2, r3}
    for _, v := range rects {
        wg.Add(1)
        go v.area(&wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}
```

这里声明了结构体类型`rect`，即长方形类型，并在该结构体类型上定义了方法`area`用来计算面积。该方法内需要判断入参的合法性，即是否都是非负数。合法性判断完成后才打印输出长方形的面积。`area`方法内部有三个分支，`r.length < 0`、`r.width < 0`以及正常情况。但是每个分支在执行完对应逻辑时都需要调用`wg.Done()`来通知协程执行完毕，接着调用`return`退出方法的执行。这种场景非常适合用`defer`，因为我们无论执行哪个分支都需要在`return`前调用`wg.Done()`，因此可以将所有分支中的`wg.Done()`抽出来放到`defer`语句中。这样代码可读性更高，而且容易维护。 下面我们用`defer`重构下：

```go
package main

import (
    "fmt"
    "sync"
)

type rect struct {
    length int
    width  int
}

func (r rect) area(wg *sync.WaitGroup) {
    defer wg.Done()
    if r.length < 0 {
        fmt.Printf("rect %v's length should be greater than zero\n", r)
        return
    }
    if r.width < 0 {
        fmt.Printf("rect %v's width should be greater than zero\n", r)
        return
    }
    area := r.length * r.width
    fmt.Printf("rect %v's area %d\n", r, area)
}

func main() {
    var wg sync.WaitGroup
    r1 := rect{-67, 89}
    r2 := rect{5, -67}
    r3 := rect{8, 9}
    rects := []rect{r1, r2, r3}
    for _, v := range rects {
        wg.Add(1)
        go v.area(&wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}
```

该程序输出如下：

```
rect {8 9}'s area 72
rect {5 -67}'s width should be greater than zero
rect {-67 89}'s length should be greater than zero
All go routines finished executing
```

这里使用`defer`还有另外一个好处，假设我们后面又新增了一个判断分支，如果不是用`defer`，那么我们还得留心加上语句`wg.Done()`。因此这种场景还是使用`defer`更好，代码的可读性、可扩展性都比较好。
