# [译] part 29: golang defer

## 什么是 Defer

Defer 用于在存在 defer 语句的函数返回之前执行函数调用。定义可能看起来很复杂，但通过示例来理解它非常简单。？？？

``````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)
}
``````

Run in playgroud

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

`largest`函数开始执行并打印上述输出的前两行。在它返回之前，`defer`函数完成执行，并打印`Finished finding largest` :)

## `defer`一个方法

`defer`不仅限于函数。`defer`调用方法也是完全合法的。让我们写一个小程序来测试它。

``````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 ")
}
``````

Run in playground

``````Welcome John Smith
``````

## `defer`的参数作用域

`defer`的函数的参数是在执行`defer`语句时传入的，在实际函数调用的时候`defer`函数的参数还是当初传入的参数。

``````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)

}
``````

Run in playgroud

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

## 多个`defer`函数的调用顺序

``````package main

import (
"fmt"
)

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

Run in playgroud

``````Orignal String: Naveen
Reversed String: neevaN
``````

## `defer`的实际用法

`defer`用于应该执行函数调用的地方，而不管代码流程如何???。让我们用一个使用`WaitGroup`的例子来理解这一点。我们将首先编写程序而不使用`defer`，然后我们将修改它以使用`defer`，以此来理解`defer`是多么有用。

``````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 {
go v.area(&wg)
}
wg.Wait()
fmt.Println("All go routines finished executing")
}
``````

Run in playground

`main`函数创建了 3 个类型为`rect`的变量`r1``r2``r3`，将它们添加到`rects`切片中。然后使用`for range`循环迭代该切片，并将`area`方法并发执行。 `WaitGroup wg`用于保证所有`Goroutines`执行完毕。`WaitGroup`作为参数传递给`area`方法，并在`area`方法中调用`wg.Done`，主要通知`main``Goroutine`已完成其工作。如果您仔细观察，可以看到这些调用恰好在`area`方法返回之前发生。无论代码采用哪个条件分支执行，都应在方法返回之前调用`wg.Done`，因此可以通过`defer`来解决这种场景。

``````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 {
go v.area(&wg)
}
wg.Wait()
fmt.Println("All go routines finished executing")
}
``````
``````rect {8 9}'s area 72
rect {-67 89}'s length should be greater than zero
rect {5 -67}'s width should be greater than zero
All go routines finished executing
``````

`defer`不仅能让程序简洁使用，在上述例子还有一个优点。假设我们使用新的`if`条件向`area`方法添加另一个处理分支。如果没有`defer``wg.Done`的调用，我们必须小心确保在这个新的处理分支中调用`wg.Done`。但由于对`wg.Done`的调用用了`defer`，我们再也不用担心这种情况了。相似的应用场景应该还有很多，比如打开文件的关闭等等。但是需要注意的是，大量的使用`defer`函数会导致程序运行效率变低。

0 回复

• 请尽量让自己的回复能够对别人有帮助
• 支持 Markdown 格式, **粗体**、~~删除线~~、``单行代码``
• 支持 @ 本站用户；支持表情（输入 : 提示），见 Emoji cheat sheet
• 图片支持拖拽、截图粘贴等方式上传

## 什么是 Defer

Defer 用于在存在 defer 语句的函数返回之前执行函数调用。定义可能看起来很复杂，但通过示例来理解它非常简单。？？？

``````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)
}
``````

Run in playgroud

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

`largest`函数开始执行并打印上述输出的前两行。在它返回之前，`defer`函数完成执行，并打印`Finished finding largest` :)

## `defer`一个方法

`defer`不仅限于函数。`defer`调用方法也是完全合法的。让我们写一个小程序来测试它。

``````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 ")
}
``````

Run in playground

``````Welcome John Smith
``````

## `defer`的参数作用域

`defer`的函数的参数是在执行`defer`语句时传入的，在实际函数调用的时候`defer`函数的参数还是当初传入的参数。

``````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)

}
``````

Run in playgroud

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

## 多个`defer`函数的调用顺序

``````package main

import (
"fmt"
)

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

Run in playgroud

``````Orignal String: Naveen
Reversed String: neevaN
``````

## `defer`的实际用法

`defer`用于应该执行函数调用的地方，而不管代码流程如何???。让我们用一个使用`WaitGroup`的例子来理解这一点。我们将首先编写程序而不使用`defer`，然后我们将修改它以使用`defer`，以此来理解`defer`是多么有用。

``````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 {
go v.area(&wg)
}
wg.Wait()
fmt.Println("All go routines finished executing")
}
``````

Run in playground

`main`函数创建了 3 个类型为`rect`的变量`r1``r2``r3`，将它们添加到`rects`切片中。然后使用`for range`循环迭代该切片，并将`area`方法并发执行。 `WaitGroup wg`用于保证所有`Goroutines`执行完毕。`WaitGroup`作为参数传递给`area`方法，并在`area`方法中调用`wg.Done`，主要通知`main``Goroutine`已完成其工作。如果您仔细观察，可以看到这些调用恰好在`area`方法返回之前发生。无论代码采用哪个条件分支执行，都应在方法返回之前调用`wg.Done`，因此可以通过`defer`来解决这种场景。

``````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 {
``````rect {8 9}'s area 72
`defer`不仅能让程序简洁使用，在上述例子还有一个优点。假设我们使用新的`if`条件向`area`方法添加另一个处理分支。如果没有`defer``wg.Done`的调用，我们必须小心确保在这个新的处理分支中调用`wg.Done`。但由于对`wg.Done`的调用用了`defer`，我们再也不用担心这种情况了。相似的应用场景应该还有很多，比如打开文件的关闭等等。但是需要注意的是，大量的使用`defer`函数会导致程序运行效率变低。