区分发生恢复的恐慌和没有发生错误

Stu*_*art 2 go

我有以下代码:

package main

import (
    "fmt"
)

func recoverFoo() {
    if r := recover(); r != nil {
        println("Recovered")
    }
}
func foo() (int, error) {
    defer recoverFoo()
    panic("shit!")
}
func main() {
    x, err := foo()
    println("after foo x = " + fmt.Sprint(x))

    if err != nil {
        println("An error occured")
    } else {
        println("No error occured")
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我正在调用 foo (实际上,我的函数 foo 正在调用第三方库,该库有时会出现恐慌,但有时也会返回错误)。如果它出现恐慌,我不能让它使应用程序崩溃,但我需要知道出了什么问题,因为我必须在错误时写入本地存储。

在这种情况下,虽然从 Foo 返回的值x可以有一个有效值 0。所以恢复设置 x 和 err 为其默认值(0 和 nil),并不能告诉我是否确实发生了错误......

我看到两种可能的解决方案,(1)我将 err 和 x 包装到自定义返回类型中,并假设如果其为零则发生错误。(2) 我有第三个返回布尔值,指定没有发生恐慌(默认为false

他们是我在错误处理和从恐慌中恢复方面所缺少的东西吗?我是新来的所以想要一些建议。

bla*_*een 5

由于恐慌和“软”错误都是程序异常,因此您应该保留非零错误语义。您可以将错误包装在自定义类型或简单的错误变量中,并在函数调用后进行检查。

另外,为了实际修改返回的错误,您还应该:

  • recover()在延迟函数中使用
  • 使用命名返回参数

从规范延迟语句

例如,如果延迟函数是函数文字,并且周围函数具有在文字范围内的命名结果参数,则延迟函数可以在返回结果参数之前访问和修改结果参数

package main

import (
    "errors"
    "fmt"
    "log"
)

var ErrPanicRecovered = errors.New("recovered from panic")

// named return parameters
func recoverableFoo() (i int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("%w: %v", ErrPanicRecovered, r)
        }
    }()
    // panic("problem!") // or any call that may panic; uncomment to test
    return 1, nil
}


func main() {
    x, err := foo()
    if err != nil {
        if errors.Is(err, ErrPanicRecovered) {
            log.Fatal("panicked: ", err)
        }
        log.Printf("some other error: %s", err.Error())
        return
    }

    fmt.Println("after foo x = " + fmt.Sprint(x))
}
Run Code Online (Sandbox Code Playgroud)

特别是,fmt.Errorf%w格式化动词一起使用可以让您正确包装错误,然后使用以下命令检查它errors.Is

如果格式说明符包含%w带有错误操作数的动词,则返回的错误将实现返回操作数的 Unwrap 方法。

游乐场:https://play.golang.org/p/p-JI1B0cw3x