Go从入门到精通——计算中的异常处理

计算中的异常处理

  Go 语言处理程序运行异常的方法与目前大多数主流开发语言都有所不同,不是使用常见的 try-catch 代码块的方式,而是使用了 panic-defer-recover 机制。

1、Go语言的匿名函数

  Go 语言中文支持匿名函数(anonymous function),这是一个高级机制,可以把函数当作一个值并赋值给变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
 
import (
    "fmt"
)
 
func main() {
    a := func() {
        fmt.Println("TEST")
    }
    a() //调用 func(),函数中打印一次 "TEST"
    var b func()
    b = a
    b() //调用 func(),函数中打印一次 "TEST"
}

  将一个函数赋值给了变量 a,这是一个匿名函数的基本用法。

  由于匿名函数,所以 func 关键字之后没有函数名而是表示函数参数声明的圆括号。由于本例中的函数不需要参数,所以圆括号内不需要任何内容,函数体的花括号内仅调用 fmt.Println 函数输出一个字符串 "TEST"。由于此时变量 a 已经是一个函数类型变量,它的值就是一个函数,所以可以直接用 a() 的形式来调用该函数,即用变量名加上表示函数参数声明的圆括号。

注意:

  匿名函数既不需要参数也没有返回值,因此函数类型的变量声明仅写 func() 就可以了,如果有参数或返回值,需要加上相应的内容。  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
 
import (
    "fmt"
)
 
func main() {
    a := func(s string) string {
        fmt.Println("收到" + s)
        return "OK"      // 如果缺少return的话,会报错:missing return at end of function
    }
    a("TEST")
    var b func(string) string
    b = a
    b("nice")
}

  上述代码是赋值给变量a的匿名函数带有一个参数并有返回值,除了没有变量名之外,其他部分和普通函数的写法都是一样的。调用该函数的时候,需要在圆括号里加上传入的参数值。 

  需要注意的是,声明该函数类型的变量,该类型的函数接受一个字符串类型的参数并返回一个字符串类型的返回值。

  与定义该匿名函数时的区别是:不需要写出参数的名字,只有在定义函数时代码中用到参数才需要参数名。调用变量b中函数的方法也一样,只是加上所需的参数。

  需要注意的是,本例调用匿名函数时都没有用到该函数的返回值,实际上是丢弃了该返回值。这是允许的。如果需要用到返回值,可以用类似下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
 
import (
    "fmt"
)
 
func main() {
    a := func(s string) string {
        fmt.Println("收到" + s)
        return "OK"
    }
    r := a("TEST")
    fmt.Printf("%v", r)
}

  另外,匿名函数还可以定义后直接运行,只需要在定义时加上后面的圆括号和所需参数:

1
2
3
4
5
6
7
8
9
10
11
12
package main
 
import (
    "fmt"
)
 
func main() {
    func(s string) string {
        fmt.Println("收到" + s)
        return "OK"
    }("TEST")
}

  上述代码中并未将定义的匿名函数赋值给某个变量,而是直接在函数体后加上圆括号和所需的参数,这样会导致定义完的函数立即被执行。这也是允许的用法,在 Go 语言中异常处理时也常会用到该方法。

2、Go 语言的延迟处理机制

  Go 语言中有一个关键字 defer,它后面需要且只能跟着一个函数调用。

  defer 语句会在其所处的函数自然终止或异常中止时被按照定义时的反序依次调用,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
 
import (
    "fmt"
)
 
func main() {
    defer func() {
        fmt.Println("func1")
    }()
 
    defer println("func2")
    println("main")
}

  上述代码的main函数里编写了两个 defer 语句。

  其中第一条 defer 语句采用了匿名函数定义后立即调用的形式,该匿名函数内仅是调用内置函数 println 来输出字符串 "func1";第二条 defer 语句则直接调用了 println 来输出字符串 "func2"。之后又用普通的形式调用 println 输出 "main"。

  可以发现,defer 语句所指定的函数调用并不会按照正常的代码语句顺序执行,因此输出字符串 "main" 的语句先被执行;在 main 函数执行完毕后,先执行输出字符串 "func2" 的 defer 语句,然后才执行输出字符串 "func1" 的 defer 语句,按照 "先定义的后执行" 的规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
 
import (
    f "fmt"
)
 
func add(a, b int) int {
 
    defer func() {
        f.Println("在此处defer")
    }()
 
    f.Println("先执行本句")
    return a + b
}
func main() {
    f.Println(add(1, 2))
}

  代码中在自定义的 add 函数中定义了 defer 语句,从代码执行的结果可以看出,defer 语句是在 add 函数体内部代码的最后被执行的。另外,如果函数中有其他 return 语句返回,defer 语句也会在 return之前被执行。

  前面介绍的是函数正常结束或返回的情况,即使出现异常,defer 语句也会被执行。

posted @   左扬  阅读(136)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2018-09-03 Centos7——docker持久化存储和卷间状态共享(笔记)
levels of contents
点击右上角即可分享
微信分享提示