在Golang捕捉恐慌

时间:2014-07-29 21:42:14

标签: exception-handling error-handling go

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

我怎样才能抓住'这种恐慌并在直接传递给它的东西(os.Args[1])时会引起恐慌?就像PHP中的try / catch或者Python中的try / except一样。

我在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)
}

7 个答案:

答案 0 :(得分:84)

恐慌程序可recover使用内置recover()功能:

  

recover功能允许程序管理恐慌goroutine的行为。假设函数G推迟调用D的函数recover,并且在G正在执行的同一goroutine上的函数中发生恐慌。当延迟函数的运行达到D时,Drecover的调用的返回值将是传递给panic调用的值。如果D正常返回,则无需启动新的panic,恐慌序列就会停止。在这种情况下,在Gpanic调用之间调用的函数的状态将被丢弃,并恢复正常执行。然后运行在G之前由D推迟的任何函数,并通过返回其调用者来终止G的执行。

     

如果满足以下任何条件,则恢复的返回值为零:

     
      
  • panic的论点是nil;
  •   
  • goroutine并不恐慌;
  •   
  • 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范例是明确检查错误。如果在普通程序执行期间没有发生恐慌的情况,程序应该只是恐慌。例如,无法打开文件是可能发生的事情,不应该在内存耗尽时引起恐慌值得恐慌。尽管如此,这种机制的存在是为了能够捕捉到这些情况,并且可能会优雅地关闭。

答案 1 :(得分:39)

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)
}

答案 2 :(得分:14)

第一:你不想这样做。 Try-catch式错误处理不是错误处理。在Go中,您将首先检查len(os.Args)并仅在存在时访问元素1.

对于极少数情况,您需要捕捉恐慌(并且您的情况其中之一!)将deferrecover结合使用。见http://golang.org/doc/effective_go.html#recover

答案 3 :(得分:13)

一些Golang官方软件包使用 panic / defer + recover 作为 throw / catch ,但仅限于他们需要解除大型调用堆栈时。在 Golang的json包中使用 panic / defer + recover 作为 throw / catch 是最优雅的解决方案。

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

  

有关恐慌和恢复的真实示例,请参阅Go标准库中的 json包。它使用一组递归函数对JSON编码的数据进行解码。当遇到格式错误的JSON时, 解析器调用panic将堆栈展开到顶级函数调用,该调用从恐慌中恢复 并返回相应的错误值(请参阅解码错误'以及解码解码方法解码解码方法。

搜索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)
}

答案 4 :(得分:2)

我不得不在测试案例中发现恐慌。我被重定向到了这里。

  

func.go

long lastPollTime = object != null ?object.getTime():0;
  

func_test.go

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

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

答案 5 :(得分:2)

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

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

答案 6 :(得分:1)

请注意,恐慌 Execution 错误的恢复处理(例如尝试索引数组超出界限触发器)可能会在issue 14965

之后改变为1.7

请参阅CL 21214its test

  

运行时:执行错误恐慌值实现Error接口

     

执行恐慌实现错误由Run-time panics (specs)强制执行,而不是使用字符串进行恐慌。

当你恢复恐慌错误时,你可以这样做:

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

目前仍在评估中,Dave Cheney。提到:

  

我不知道人们目前在做什么但是从我的POV中已经被打破了很长时间而且没有人抱怨所以他们要么明确地依赖于破碎的行为,要么没人关心。无论哪种方式,我认为避免做出这种改变是个好主意。