Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,也就是说,遇到真正的异常的情况下(比如除数为0了)。才使用Go中引入的Exception处理:defer, panic, recover。

这几个异常的使用场景可以这么简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理

主要参考:https://blog.golang.org/defer-panic-and-recover

package main

import "fmt"

func main() {
f()
fmt.Println("Returned normally from f.")
} func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
} func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}

The Go Blog

Defer, Panic, and Recover

4 August 2010

Go has the usual mechanisms for control flow: if, for, switch, goto. It also has the go statement to run code in a separate goroutine. Here I'd like to discuss some of the less common ones: defer, panic, and recover.

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.

For example, let's look at a function that opens two files and copies the contents of one file to the other:

func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
} dst, err := os.Create(dstName)
if err != nil {
return
} written, err = io.Copy(dst, src)
dst.Close()
src.Close()
return
}

This works, but there is a bug. If the call to os.Create fails, the function will return without closing the source file. This can be easily remedied by putting a call to src.Close before the second return statement, but if the function were more complex the problem might not be so easily noticed and resolved. By introducing defer statements we can ensure that the files are always closed:

func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close() dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close() return io.Copy(dst, src)
}

Defer statements allow us to think about closing each file right after opening it, guaranteeing that, regardless of the number of return statements in the function, the files will be closed.

The behavior of defer statements is straightforward and predictable. There are three simple rules:

1. A deferred function's arguments are evaluated when the defer statement is evaluated.

In this example, the expression "i" is evaluated when the Println call is deferred. The deferred call will print "0" after the function returns.

func a() {
i := 0
defer fmt.Println(i)
i++
return
}

2. Deferred function calls are executed in Last In First Out order after the surrounding function returns.

This function prints "3210":

func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}

3. Deferred functions may read and assign to the returning function's named return values.

In this example, a deferred function increments the return value i after the surrounding function returns. Thus, this function returns 2:

func c() (i int) {
defer func() { i++ }()
return 1
}

This is convenient for modifying the error return value of a function; we will see an example of this shortly.

Panic is a built-in function that stops the ordinary flow of control and begins panicking. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller. To the caller, F then behaves like a call to panic. The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes. Panics can be initiated by invoking panic directly. They can also be caused by runtime errors, such as out-of-bounds array accesses.

Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.

Here's an example program that demonstrates the mechanics of panic and defer:

package main

import "fmt"

func main() {
f()
fmt.Println("Returned normally from f.")
} func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
} func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}

The function g takes the int i, and panics if i is greater than 3, or else it calls itself with the argument i+1. The function f defers a function that calls recover and prints the recovered value (if it is non-nil). Try to picture what the output of this program might be before reading on.

The program will output:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

If we remove the deferred function from f the panic is not recovered and reaches the top of the goroutine's call stack, terminating the program. This modified program will output:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
panic: 4 panic PC=0x2a9cd8
[stack trace omitted]

For a real-world example of panic and recover, see the json package from the Go standard library. It decodes JSON-encoded data with a set of recursive functions. When malformed JSON is encountered, the parser calls panic to unwind the stack to the top-level function call, which recovers from the panic and returns an appropriate error value (see the 'error' and 'unmarshal' methods of the decodeState type in decode.go).

The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values.

Other uses of defer (beyond the file.Close example given earlier) include releasing a mutex:

mu.Lock()
defer mu.Unlock()

printing a footer:

printHeader()
defer printFooter()

and more.

In summary, the defer statement (with or without panic and recover) provides an unusual and powerful mechanism for control flow. It can be used to model a number of features implemented by special-purpose structures in other programming languages. Try it out.

By Andrew Gerrand

-------------------------------------------------------------

没有符合的翻译结果!
请确认选中的文本是完整的英语单词或句子。
轻灵划译
数据来源:

go panic recover 异常处理的更多相关文章

  1. Go语言异常处理defer\panic\recover

    Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱.因为开发者很容易滥用异常, ...

  2. Go的异常处理 defer, panic, recover

    Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱.因为开发者很容易滥用异常, ...

  3. [Go] 如何正确地 抛出 错误 和 异常(error/panic/recover)?

    序言 错误 和 异常 是两个不同的概念,非常容易混淆.很多程序员习惯将一切非正常情况都看做错误,而不区分错误和异常,即使程序中可能有异常抛出,也将异常及时捕获并转换成错误.从表面上看,一切皆错误的思路 ...

  4. defer,panic,recover

    Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱.因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个 ...

  5. 探究 Go 源码中 panic & recover 有哪些坑?

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/627 本文使用的go的源码1.17.3 前言 写这一篇文章的原因是 ...

  6. go语言defer panic recover用法总结

    defer defer是go提供的一种资源处理的方式.defer的用法遵循3个原则 在defer表达式被运算的同时,defer函数的参数也会被运算.如下defer的表达式println运算的同时,其入 ...

  7. defer, panic, recover使用总结

    1. defer : 延迟调用.多个defer,依次入栈,在函数即将退出时,依次出栈调用 package main import "fmt" func main() { defer ...

  8. Go panic recover

    panic 1. 停止当前函数执行 2. 一直向上返回,执行每一层的defer 3. 如果没有遇到recover, 程序退出 recover 1. 仅在defer调用中使用 2. 获取panic的值 ...

  9. 闭包 panic recover

    闭包=函数+外层变量的引用 recover必须搭配defer使用 defer一定要在可能引发panic的语句之前定义

随机推荐

  1. 【POJ2516】Minimum Cost

    [POJ2516]Minimum Cost 题意:有N个收购商.M个供应商.K种物品.对于每种物品,每个供应商的供应量和每个收购商的需求量已知.每个供应商与每个收购商之间运送该物品的运费已知.求满足收 ...

  2. Android实现“退出确认”对话框

    @Override public void onBackPressed() { new AlertDialog.Builder(this).setTitle("确认退出吗?") . ...

  3. pageResponse - 让H5适配移动设备全家

    http://www.cnblogs.com/PeunZhang/p/4517864.html

  4. kettle中denormalizer(列转行)的使用

    转载: 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://sucre.blog.51cto.com/1084905/1434015 ...

  5. R中,将从MySQL中获取的结果字符列表转化为向量,并测试绘制图形

    # 使用RMySQL操作数据库 # 载入DBI和RMySQL包 library(DBI) library(RMySQL) # 创建数据库连接 con <- dbConnect(MySQL(),h ...

  6. distinct用group by优化

    当数据量非常大,在同一个query中计算多个不相关列的distinct时,往往很容易出现数据倾斜现象,导致运行半天都不能得到结果. 比如以下的SQL语句(a, b, c没有相关性): select d ...

  7. Struts2中获取Web元素request、session、application对象的四种方式

    我们在学习web编程的时候,一般都是通过requet.session.application(servletcontext)进行一系列相关的操作,request.session.和applicatio ...

  8. 给Cell间隔颜色

    ==){ cell.backgroundColor = [UIColor colorWithRed:. green:. blue:. alpha:]; } else{ cell.backgroundC ...

  9. PHP Version 7.0.13-0ubuntu0.16.04.1 mysql-server-5.7

    https://zh.wikipedia.org/wiki/Linux发行版 一个典型的Linux桌面发行版包括一个Linux 内核,来自GNU的工具和库,和附加的软件.文档,还有一个窗口系统,窗口管 ...

  10. Cookies Client Identification

    HTTP The Definitive Guide Cookies are the best current way to identify users and allow persistent se ...