目录

Golang中使用defer语句修改返回值会发生什么?

探究在 defer 中修改返回值,对结果是否产生变化


无名返回值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Result: 1
func test01() int {
	ret := 1
	defer func() {
		ret++
	}()
	return ret
}


// Result: 1
func test02() int {
	ret := 1
	defer func(ret int) {
		ret++
	}(ret)
	return ret
}

其中ret在函数内定义,是一个局部变量,此时执行return ret时,函数的返回值就等于了ret = 1,因此后续在defer语句中,无论如何修改ret的值,返回值不变。

有名返回值

1
2
3
4
5
6
7
8
// Result: 2
func test03() (ret int) {
	ret = 1
	defer func() {
		ret++
	}()
	return ret
}

其中 ret 在函数签名时就已经定义,因此返回值就是 ret,后续对 ret 的修改,就是对返回值的修改。

1
2
3
4
5
6
7
8
// Result: 1
func test04() (ret int) {
	ret = 1
	defer func(ret int) {
		ret++
	}(ret)
	return
}

需要注意的是,在 defer 中传入 ret 参数时,此时 defer 中的 ret 为形参,指向的是一个新的内存地址,因此不会对返回值进行影响。

返回值为指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Result: 10
func test05() *int {
	var ret int
	defer func() {
		ret = 10
	}()
	return &ret
}

// Result: 0
func test06() *int {
	var ret int
	defer func(ret int) {
		ret = 10
	}(ret)
	return &ret
}

// Result: 10
func test07() *int {
	var ret int
	defer func(ret *int) {
		*ret = 10
	}(&ret)
	return &ret
}

此时函数的返回值为指向ret的指针,后续对ret进行内容上的修改,指针指向ret的内容,因此返回值会因为defer的操作而改变。

同时需要注意,在defer里如果没有传入指针,而是ret的形参时,由于拷贝赋值,修改的不是ret所指向的内存空间,因此返回值不变。

总结

defer 对返回值修改的情况:

  • 无名返回值:不会修改返回值;
  • 有名返回值:如果在 defer 里修改返回值,并且以闭包的形式修改,那么返回值会被修改;
  • 返回值为指针:如果修改返回值指向的内存空间,那么 defer 会修改返回值。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package defer_test

import (
	"testing"
)

func test01() int {
	ret := 1
	defer func() {
		ret++
	}()
	return ret
}

func test02() int {
	ret := 1
	defer func(ret int) {
		ret++
	}(ret)
	return ret
}

func test03() (ret int) {
	ret = 1
	defer func() {
		ret++
	}()
	return ret
}

func test04() (ret int) {
	ret = 1
	defer func(ret int) {
		ret++
	}(ret)
	return
}

func test05() *int {
	var ret int
	defer func() {
		ret = 10
	}()
	return &ret
}

func test06() *int {
	var ret int
	defer func(ret int) {
		ret = 10
	}(ret)
	return &ret
}

func test07() *int {
	var ret int
	defer func(ret *int) {
		*ret = 10
	}(&ret)
	return &ret
}

func TestDefer(t *testing.T) {
	t.Logf("test01: %d", test01())
	t.Logf("test02: %d", test02())
	t.Logf("test03: %d", test03())
	t.Logf("test04: %d", test04())
	t.Logf("test05: %d", *test05())
	t.Logf("test06: %d", *test06())
	t.Logf("test07: %d", *test07())
}

执行结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
=== RUN   TestDefer
    defer_test.go:64: test01: 1
    defer_test.go:65: test02: 1
    defer_test.go:66: test03: 2
    defer_test.go:67: test04: 1
    defer_test.go:68: test05: 10
    defer_test.go:69: test06: 0
    defer_test.go:70: test07: 10
--- PASS: TestDefer (0.00s)
PASS