Catching panics in Golang

后端 未结 7 2009
自闭症患者
自闭症患者 2021-01-31 07:24

With the following code, if no file argument is given, a panic is thrown for line 9 panic: runtime error: index out of range as expected.

How can I \'catch\

相关标签:
7条回答
  • 2021-01-31 07:41

    First: You wouldn't want to do this. Try-catch-style error handling is no error handling. In Go you would check len(os.Args) first and access element 1 only if present.

    For the rare cases you need to catch panics (and your case is not one of them!) use defer in combination with recover. See http://golang.org/doc/effective_go.html#recover

    0 讨论(0)
  • 2021-01-31 07:43

    Some Golang official packages use panic/defer+recover as throw/catch, but only when they need to unwind a large call stack. In Golang's json package using panic/defer+recover as throw/catch is the most elegant solution.

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

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

    Search for d.error( at http://golang.org/src/encoding/json/decode.go

    In your example the "idiomatic" solution is to check the parameters before using them, as other solutions have pointed.

    But, if you want/need to catch anything you can do:

    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)
    }
    
    0 讨论(0)
  • 2021-01-31 07:48

    Note that the recover treatment of a panic Execution error (such as attempting to index an array out of bounds trigger) might change with go 1.7 after issue 14965

    See CL 21214 and its test:

    runtime: make execution error panic values implement the Error interface

    Make execution panics implement Error as mandated by Run-time panics (specs), instead of panics with strings.

    When you recover a panic error, you would be able to do:

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

    This is still being evaluated, and as Dave Cheney. mentions:

    I don't know what people are currently doing but from my POV this has been broken for a long time and nobody has complained so they are either explicitly relying on the broken behaviour, or nobody cares. Either way I think it's a good idea to avoid making this change.

    0 讨论(0)
  • 2021-01-31 07:51

    I had to catch panics in a test case. I got redirected here.

    func.go

    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
    }
    

    Used from https://github.com/golang/go/commit/e4f1d9cf2e948eb0f0bb91d7c253ab61dfff3a59 (ref from VonC)

    0 讨论(0)
  • 2021-01-31 07:52

    A panicking program can recover with the builtin recover() function:

    The recover function allows a program to manage behavior of a panicking goroutine. Suppose a function G defers a function D that calls recover and a panic occurs in a function on the same goroutine in which G is executing. When the running of deferred functions reaches D, the return value of D's call to recover will be the value passed to the call of panic. If D returns normally, without starting a new panic, the panicking sequence stops. In that case, the state of functions called between G and the call to panic is discarded, and normal execution resumes. Any functions deferred by G before D are then run and G's execution terminates by returning to its caller.

    The return value of recover is nil if any of the following conditions holds:

    • panic's argument was nil;
    • the goroutine is not panicking;
    • recover was not called directly by a deferred function.

    Here is an example of how to use this:

    // 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]
    }
    

    Notice that more often than not, panicking is not the right solution. The Go paradigm is to check for errors explicitly. A program should only panic if the circumstances under which it panics do not happen during ordinary program executing. For instance, not being able to open a file is something that can happen and should not cause a panic while running out of memory is worth a panic. Nevertheless, this mechanism exists to be able to catch even these cases and perhaps shut down gracefully.

    0 讨论(0)
  • 2021-01-31 07:59

    We can manage panic without halting process using recover. By calling recover in any function using defer it will return the execution to calling function. Recover returns two values one is boolean and other one is interface to recover. Using type assertion we can get underlying error value You can also print underlying error using recover.

    defer func() {
        if r := recover(); r != nil {
            var ok bool
            err, ok = r.(error)
            if !ok {
                err = fmt.Errorf("pkg: %v", r)
            }
        }
    }()
    
    0 讨论(0)
提交回复
热议问题