Golang
主页 > 脚本 > Golang >

go的defer和return的执行顺序介绍

2024-07-14 | 佚名 | 点击:

go的defer和return的执行顺序

go的defer和return是golang中的两个关键字,return用于返回函数的返回值,也可以参与一定的流程控制,比如下面代码,return短路了后面的输出

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package main

 

import "fmt"

 

// defer 和 return的详解

func main() {

    foo(2)

    foo(1)

}

func foo(i int) {

    fmt.Println(i)

    if i == 1 {

        return

    }

    fmt.Println(i + 1)

}

结果:

2
3
1

第一次输出完整的输出了i和i+1,第二次输出被短路,只输出了1

defer是golang中的延迟调用,经常用于文件流的关闭,锁的解锁操作,defer后面的操作会在当前函数或者goroutine结束之后进行调用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package main

 

import "fmt"

 

// defer 和 return的详解

func main() {

    foo()

}

func foo() {

    defer fmt.Println("println defer")

    fmt.Println("println foo")

}

 

输出:

println foo

println defer

defer自身有一些特性,比如defer和defer之间的执行顺序是先进后出,先defer的最后执行,分析下面代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package main

 

import "fmt"

 

// defer 和 return的详解

func main() {

    foo()

}

func foo() {

    defer fmt.Println("floor 3")

    defer fmt.Println("floor 2")

    fmt.Println("floor 1")

}

 

输出:

floor 1

floor 2

floor 3

根据这一特性,如果我们defer调用的代码中存在panic 的可能性,为了保证系统的运行,我们应该在前面recover而不是后面

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

ackage main

 

import "fmt"

 

// defer 和 return的详解

func main() {

    foo()

}

func foo() {

    defer func() {

        panic("panic test")

    }()

    defer func() {

        if err := recover(); err != nil {

            fmt.Println("catch panic:", err)

        }

    }()

}

 

输出:

panic: panic test

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

package main

 

import "fmt"

 

// defer 和 return的详解

func main() {

    foo()

}

func foo() {

    defer func() {

        if err := recover(); err != nil {

            fmt.Println("catch panic:", err)

        }

    }()

    defer func() {

        panic("panic test")

    }()

}

输出:

catch panic: panic test

defer和return的相互影响

defer和return的相互影响,主要是在返回值上表现,考虑下面代码,输出应该是什么:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

import "fmt"

 

// defer 和 return的详解

func main() {

    fmt.Println(foo1())

    fmt.Println(foo2())

    fmt.Println(foo3())

}

func foo1() int {

    i := 1

    defer func() { i++ }()

    return i

}

func foo2() (i int) {

    i = 1

    defer func() { i++ }()

    return i

}

func foo3() (i int) {

    defer func() { i++ }()

    return 1

}

输出:

1
2
2

导致上面情况的原因是

在 foo1 函数中,defer 语句中的闭包会在函数返回后执行,但是此时返回值已经确定为 1 ,所以最终返回 1 。

在 foo2 函数中,使用了命名返回值 i 。defer 语句中的闭包修改的是这个命名返回值,所以返回 2 。

在 foo3 函数中,同样使用了命名返回值 i ,defer 语句中的闭包修改了这个命名返回值,并且函数直接返回 1 ,但 defer 中的修改使得最终返回 2 。

而return的另一个特性,也会影响return和defer中代码的执行顺序

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

package main

 

import "fmt"

 

// defer 和 return的详解

func main() {

    fmt.Println(foo1())

 

}

func foo1() int {

    defer func() { fmt.Println("This is defer") }()

    return func() int {

        fmt.Println("This is return")

        return 1

    }()

}

 

输出:

This is return

This is defer

1

导致上面输出的原因是,return是非原子性的,defer会在return返回值之前执行,但return中的语句,会被全部执行,直到return锚定了某个值或者命名返回值,然后执行defer语句,最后返回return锚定的这个值

原文链接:
相关文章
最新更新