在Golang捕捉恐慌

Luk*_*e B 45 error-handling exception-handling go

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

我怎样才能"抓住"这种恐慌并在直接传递某些东西时处理它(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)
}
Run Code Online (Sandbox Code Playgroud)

fuz*_*fuz 92

恐慌程序可以使用内置函数恢复recover():

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

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

  • 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]
}
Run Code Online (Sandbox Code Playgroud)

请注意,恐慌通常不是正确的解决方案.Go范例是明确检查错误.如果在普通程序执行期间没有发生恐慌的情况,程序应该只是恐慌.例如,无法打开文件是可能发生的事情,不应该在内存耗尽时引起恐慌值得恐慌.尽管如此,这种机制的存在是为了能够捕捉到这些情况并且可能优雅地关闭.

  • 请在downvoting之前发表评论.你为什么要求我回答OP问的字面问题?OP明确表示他希望从恐慌中恢复过来.我写了一个答案如何做到这一点.这是否是正确的做法不是我的判断. (34认同)
  • 供将来参考(因为没有人提到过):吸入果冻豆和/或芥末是一个坏主意.针对您遇到的任何问题,请考虑采用不同的方法. (27认同)
  • @FUZxxl:你正在推广糟糕的风格.如果有人问:"我想用芥末吸入果冻豆.我该怎么做?" 你会推荐一个解决方案吗?或者也许给出一个很好的建议,他的愿望可能是一个坏主意,并解释如何使用果冻豆和芥末? (9认同)
  • @Volker如果他问这个问题,我会给他一个诚实的回答.我可能会在另外的评论中指出,这可能不是最聪明的事情.我是谁来判断别人的问题? (6认同)
  • 来到这个问题是为了了解如何处理 go 中的恐慌。这应该是公认的答案。 (4认同)
  • 恐慌只有当你知道你无法继续,只会崩溃.崩溃可能意味着退出整个程序,从API或Web请求返回"内部错误"消息,或者结束程序的其他高级别块.恐慌是缓慢的,非惯用的,并且易于使用(特别是当你有80%的错误回报和20%的恐慌;你得到两者的缺点),所以不要将它们用于潜在的可恢复情况,如缺失/数据库中的重复对象,网络错误等.此外,如果编写库,只要*调用者*可能能够恢复,就返回错误. (3认同)
  • 或者,不精确但直观:许多程序都有一个处理程序来捕捉总是可能发生的恐慌(索引超出范围、空指针、内存不足、类型断言失败......),这样处理一个请求的意外错误就不会发生关闭服务器。这通常应该是您唯一需要的此类处理程序,并且您插入的任何 `panic()` 都应该用于解决总是冒泡到该级别的问题。 (2认同)

One*_*One 40

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)
}
Run Code Online (Sandbox Code Playgroud)

  • 这不是一个正确的答案。在任何语言中,您都应该检查可能的错误和异常。问题在于开发人员也是人,也会犯错误。问题的重点是当开发人员未能正确处理可能的错误时处理恐慌。“太糟糕了,你应该考虑一下”的答案从来没有帮助。 (3认同)
  • Minor nit:你可能想要`log.Fatal(err)`(或者在打印带有`err`的消息后可能是`os.Exit`)在最后的`if`块中. (2认同)
  • “ Go不是python,在使用它之前,您应该正确检查args:” true,但是您可能想在测试中执行类似的操作,以检查您的程序是否按需恐慌而不会引发错误。恢复似乎是完成典型的尝试捕获的方法 (2认同)

Vol*_*ker 14

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

对于极少数情况,您需要捕捉恐慌(并且您的情况不是其中之一!)defer与之结合使用recover.请参阅http://golang.org/doc/effective_go.html#recover


Luc*_*ato 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将堆栈展开到顶级函数调用,该函数调用从恐慌中恢复并返回适当的错误值(请参阅解码中的decodeState类型的'error'和'unmarshal'方法) .走).

搜索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)
}
Run Code Online (Sandbox Code Playgroud)


Sai*_*ram 5

我必须在测试用例中捕捉恐慌。我被重定向到这里。

函数go

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

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
}
Run Code Online (Sandbox Code Playgroud)

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


Him*_*shu 5

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

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