我有以下代码:
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)
他们是我在错误处理和从恐慌中恢复方面所缺少的东西吗?我是新来的所以想要一些建议。
由于恐慌和“软”错误都是程序异常,因此您应该保留非零错误语义。您可以将错误包装在自定义类型或简单的错误变量中,并在函数调用后进行检查。
另外,为了实际修改返回的错误,您还应该:
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