在 Golang 引发恐慌

使用下面的代码,如果没有给出文件参数,将按预期为第9行 panic: runtime error: index out of range引发慌乱。

我如何“捕捉”这种恐慌,并处理它时,直接传递给它的东西(os.Args[1]) ,造成恐慌?与 PHP 中的 try/catch 或者 Python 中的 try/不同。

我在 StackOverflow 上进行了搜索,但是没有找到任何答案。

package main


import (
"fmt"
"os"
)


func main() {
file, err := os.Open(os.Args[1])
if err != nil {
fmt.Println("Could not open file")
}
fmt.Printf("%s", file)
}
98473 次浏览

Go 不是 python,在使用它之前应该正确检查 args:

func main() {
if len(os.Args) != 2 {
fmt.Printf("usage: %s [filename]\n", os.Args[0])
os.Exit(1)
}
file, err := os.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", file)
}

第一,你不会想这么做的。Try-catch 风格的错误处理不是错误处理。在 Go 中,首先选中 len(os.Args),只有当存在时才访问元素1。

对于罕见的情况下,你需要赶上恐慌(你的情况是 没有其中之一!)结合使用 deferrecover。见 http://golang.org/doc/effective_go.html#recover

一个恐慌的程序可以 恢复与内置的 recover()功能:

recover函数允许程序管理一个惊慌失措的常规程序的行为。假设函数 G延迟调用 recover的函数 D,并且在执行 G的同一个 goroutine 上的函数中发生了慌乱。当延迟函数的运行到达 D时,Drecover的调用的返回值将是传递给 panic调用的值。如果 D正常返回,而没有启动新的 panic,恐慌序列就会停止。在这种情况下,将丢弃在 G和对 panic的调用之间调用的函数的状态,并恢复正常执行。然后运行 GD之前延迟的任何函数,G的执行通过返回到其调用者而终止。

如符合下列任何一项条件,则回收的回报值为零:

  • panic的论点是 nil;
  • 高鲁丁并没有感到恐慌;
  • 延迟函数没有直接调用 recover

下面是一个如何使用它的例子:

// access buf[i] and return an error if that fails.
func PanicExample(buf []int, i int) (x int, err error) {
defer func() {
// recover from panic if one occured. Set err to nil otherwise.
if (recover() != nil) {
err = errors.New("array index out of bounds")
}
}()


x = buf[i]
}

请注意,恐慌往往不是正确的解决方案。Go 范例是显式地检查错误。一个程序只有在正常程序执行过程中没有发生它感到恐慌的情况时才应该感到恐慌。例如,无法打开一个文件是可能发生的事情,不应该在内存耗尽时引起恐慌,这值得引起恐慌。尽管如此,这种机制的存在是为了能够捕捉到这些情况,或许还能够优雅地关闭。

一些 Golang 官方软件包使用 恐慌/推迟 + 恢复作为 投球/接球,但只有当它们需要解开一个大型通话堆栈时才会这样做。在 Golang 的 Json 包裹中,使用 恐慌/推迟 + 恢复作为 投球/接球是最优雅的解决方案。

http://blog.golang.org/defer-panic-and-recover

有关慌乱和恢复的实际示例,请参见 Go 标准库中的 Json 包裹。它使用一组递归函数对 JSON 编码的数据进行解码。当遇到格式不正确的 JSON 时,解析器调用 panic 将堆栈解除到顶级函数调用,该函数调用从 panic 恢复并返回一个适当的错误值(请参见 decde.go 中的 decdeState 类型的‘ error’和‘ unmaral’方法)。

搜索 d.error( Http://golang.org/src/encoding/json/decode.go

在您的示例中,“惯用”解决方案是在使用参数之前检查它们,正如其他解决方案所指出的那样。

但是,如果你想/需要赶上 什么都行,你可以这样做:

package main


import (
"fmt"
"os"
)


func main() {


defer func() { //catch or finally
if err := recover(); err != nil { //catch
fmt.Fprintf(os.Stderr, "Exception: %v\n", err)
os.Exit(1)
}
}()


file, err := os.Open(os.Args[1])
if err != nil {
fmt.Println("Could not open file")
}


fmt.Printf("%s", file)
}

请注意,在 第14965期之后,恢复处理紧急 处决错误(例如尝试索引数组越界触发器)的方法可能会随着 go 1.7的改变而改变

CL 21214它的考验:

运行时: 使执行错误应急值实现 Error接口

使执行恐慌实现 运行时恐慌(specs)规定的错误,而不是使用字符串恐慌。

当您恢复一个恐慌错误时,您可以执行以下操作:

if _, ok := recovered.(runtime.Error); !ok {

这仍在评估中,正如 Dave Cheney提到的:

我不知道人们现在在做什么,但从我的观点来看,这已经被打破了很长一段时间,没有人抱怨,所以他们要么明确地依赖于被打破的行为,或者没有人在乎。不管怎样,我认为避免做出这种改变是个好主意。

我不得不在一个测试案例中抓住恐慌。我被重定向到这里。

去吧

var errUnexpectedClose = errors.New("Unexpected Close")
func closeTransaction(a bool) {
if a == true {
panic(errUnexpectedClose)
}
}

Func _ test. go

func TestExpectedPanic() {
got := panicValue(func() { closeTransaction(true) })
a, ok := got.(error)
if a != errUnexpectedClose || !ok {
t.Error("Expected ", errUnexpectedClose.Error())
}
}


func panicValue(fn func()) (recovered interface{}) {
defer func() {
recovered = recover()
}()
fn()
return
}

用于 https://github.com/golang/go/commit/e4f1d9cf2e948eb0f0bb91d7c253ab61dfff3a59(来自 VonC 的参考文献)

我们可以使用恢复在不停止进程的情况下管理恐慌。通过在任何函数中使用 Defer 调用 Recovery,它将把执行返回给调用函数。恢复返回两个值,一个是布尔值,另一个是接口恢复。使用类型断言,我们可以得到底层的错误值 还可以使用恢复打印基础错误。

defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()