Go 语言函数

函数是 Go 里面的基本代码块,Go 函数的功能非常强大,以至于被认为拥有函数式编程语言的多种特性。由于函数内容较多,这里将会分为多个章节去讲解:

defer 和追踪

关键字 defer 允许我们在函数返回之前(或 return 语句之后)才执行某个语句或函数。

关键字 defer 的用法类似于面向对象编程语言 Java 和 C# 的 finally 语句块,它一般用于释放某些已分配的资源。

示例 defer.go:

  1. package main
  2. import "fmt"
  3. func main() {
  4. function1()
  5. }
  6. func function1() {
  7. fmt.Printf("In function1 at the top\n")
  8. defer function2()
  9. fmt.Printf("In function1 at the bottom!\n")
  10. }
  11. func function2() {
  12. fmt.Printf("Function2: Deferred until the end of the calling function!")
  13. }

输出:

  1. In Function1 at the top
  2. In Function1 at the bottom!
  3. Function2: Deferred until the end of the calling function!

你可以将 defer 关键字去掉后,再对比输出结果有什么不痛。

使用 defer 的语句同样可以接受参数,下面这个例子就会在执行 defer 语句时打印 0:

  1. func a() {
  2. i := 0
  3. defer fmt.Println(i)
  4. i++
  5. return
  6. }

当函数内存在多个 defer 关键字时,它们会以逆序执行(类似栈,后进先出):

  1. func f() {
  2. for i := 0; i < 5; i++ {
  3. defer fmt.Printf("%d ", i)
  4. }
  5. }

上面的代码将会输出:4 3 2 1 0

关键字 defer 允许我们进行一些函数执行完成后的收尾工作,例如:

1、关闭文件流

  1. // open a file
  2. defer file.Close()

2、解锁一个加锁的资源

  1. mu.Lock()
  2. defer mu.Unlock()

3、打印最终报告

  1. printHeader()
  2. defer printFooter()

4、关闭数据库链接

  1. // open a database connection
  2. defer disconnectFromDB()

合理使用 defer 语句能够使代码更加简洁。

以下代码模拟了上面描述的第 4 种情况(关闭数据库链接):

  1. package main
  2. import "fmt"
  3. func main() {
  4. doDBOperations()
  5. }
  6. func connectToDB() {
  7. fmt.Println("ok, connected to db")
  8. }
  9. func disconnectFromDB() {
  10. fmt.Println("ok, disconnected from db")
  11. }
  12. func doDBOperations() {
  13. connectToDB()
  14. fmt.Println("Defering the database disconnect.")
  15. defer disconnectFromDB() //function called here with defer
  16. fmt.Println("Doing some DB operations ...")
  17. fmt.Println("Oops! some crash or network error ...")
  18. fmt.Println("Returning from function here!")
  19. return //terminate the program
  20. // deferred function executed here just before actually returning, even if
  21. // there is a return or abnormal termination before
  22. }

输出:

  1. ok, connected to db
  2. Defering the database disconnect.
  3. Doing some DB operations ...
  4. Oops! some crash or network error ...
  5. Returning from function here!
  6. ok, disconnected from db
使用 defer 语句实现代码追踪

一个基础但十分实用的实现代码执行追踪的方案,在进入和离开某个函数时打印相关的消息,可以提炼为下面两个函数:

  1. func trace(s string) { fmt.Println("entering:", s) }
  2. func untrace(s string) { fmt.Println("leaving:", s) }

以下代码展示了何时调用这两个函数:

  1. package main
  2. import "fmt"
  3. func trace(s string) { fmt.Println("entering:", s) }
  4. func untrace(s string) { fmt.Println("leaving:", s) }
  5. func a() {
  6. trace("a")
  7. defer untrace("a")
  8. fmt.Println("in a")
  9. }
  10. func b() {
  11. trace("b")
  12. defer untrace("b")
  13. fmt.Println("in b")
  14. a()
  15. }
  16. func main() {
  17. b()
  18. }

输出:

  1. entering: b
  2. in b
  3. entering: a
  4. in a
  5. leaving: a
  6. leaving: b

上面的代码还可以修改为更加简便的版本:

  1. package main
  2. import "fmt"
  3. func trace(s string) string {
  4. fmt.Println("entering:", s)
  5. return s
  6. }
  7. func un(s string) {
  8. fmt.Println("leaving:", s)
  9. }
  10. func a() {
  11. defer un(trace("a"))
  12. fmt.Println("in a")
  13. }
  14. func b() {
  15. defer un(trace("b"))
  16. fmt.Println("in b")
  17. a()
  18. }
  19. func main() {
  20. b()
  21. }
使用 defer 语句来记录函数的参数与返回值

下面的代码展示了另一种在调试时使用 defer 语句的写法:

  1. package main
  2. import (
  3. "io"
  4. "log"
  5. )
  6. func func1(s string) (n int, err error) {
  7. defer func() {
  8. log.Printf("func1(%q) = %d, %v", s, n, err)
  9. }()
  10. return 7, io.EOF
  11. }
  12. func main() {
  13. func1("Go")
  14. }

输出:

  1. Output: 2011/10/04 10:46:11 func1("Go") = 7, EOF

分类导航