英文:
Can I recover from panic, handle the error, then panic again and keep the original stack trace?
问题
在recover中是否有可能“重新抛出”错误并保留原始的堆栈跟踪?我所知道的最好方法是再次使用panic,但这会创建一个新的堆栈跟踪。
func do() {
    defer func() {
        cleanUp()
        if x := recover(); x != nil {
            handleError()
            panic(x)
        }
    }()
    doStuff()
}
我希望实现这个的原因是,除非我的函数正常退出或handleError运行,否则我的程序会死锁。而且,如果我不保留原始的堆栈跟踪,我就不知道它在哪里崩溃。
英文:
Is it possible to "rethrow" an error from recover and keep the original stack trace? The best I know how to do is to panic again, but that does create a new stacktrace.
func do() {
    defer func() {
        cleanUp()
        if x := recover(); x != nil {
            handleError()
            panic(x)
        }
    }()
    doStuff()
}
My motivation for wanting this is that unless my function exits normally or handleError runs, my program deadlocks. And unless I preserve the original strack trace, I do not know where it crashed.
答案1
得分: 14
解决方案是不调用recover,因为这样既无法重新抛出异常,也无法访问堆栈跟踪。可以使用一个布尔标志代替recover来检查是否发生了 panic。
func do() {
    panicked := true
    defer func() {
        cleanUp()
        if panicked {
            handleError()
        }
    }()
    doStuff()
    panicked = false
}
链接:https://play.golang.org/p/PKeP9s-3tF
英文:
The solution is to not call recover, because then neither rethrowing nor accessing the stack trace is possible. Use a bool flag instead of recover to check for panic.
https://play.golang.org/p/PKeP9s-3tF
func do() {
    panicked := true
    defer func() {
        cleanUp()
        if panicked {
            handleError()
        }
    }()
    doStuff()
    panicked = false
}
答案2
得分: 6
在堆栈中较高位置的延迟函数将在发生 panic 时运行,即使它们没有调用 recover()。
只需删除 if 语句和重新 panic。然后处理错误,并让 panic 继续向上传递。
func do() {
    defer handleError()
    doStuff()
}
一个简单的示例:
func a() {
    defer func() {
        fmt.Println("a")
    }()
    panic("test")
}
func b() {
    defer func() {
        fmt.Println("b")
    }()
}
func main() {
    fmt.Println("Hello, playground")
    b()
}
输出结果:
Hello, playground
b
英文:
Defered functions higher up in the stack will run on panic, even if they don't call recover().
Simply remove the if-statement and the re-panic. Then handle your error, and let the panic continue up the stack.
func do() {
    defer handleError()
    doStuff()
}
a simple demo:
https://play.golang.org/p/UiRou5MhUR
func a() {
	defer func() {
		fmt.Println("a")
	}()
	panic("test")
}
func b() {
	defer func() {
		fmt.Println("b")
	}()
}
func main() {
	fmt.Println("Hello, playground")
	b()
}
outputs
Hello, playground
b
答案3
得分: 1
根据Daniel Conde Marin在这里的问题所提问的问题,我认为你不需要担心调用panic并传递recover()的响应对象会创建一个加密或"新的"堆栈跟踪,至少在Go 1.17中是这样的。看起来这样做只是在跟踪中添加了另一个"src/runtime/panic.go:
请参考这个示例测试:
https://goplay.space/#5i1RCJyiFJr
Go语言对待panic的方式与其他语言对待try/catch/throw/finally的方式不同。
英文:
As Daniel Conde Marin questioned here
I do not believe you need to worry about calling panic and passing in a recover()'s response object creating a cryptic or "new" stack trace, at least in go 1.17. It looks like doing so simply adds another "src/runtime/panic.go:<linenum>" frame to the trace.
See this example test:
https://goplay.space/#5i1RCJyiFJr
Go does not treat panics like other languages treat try/catch/throw/finally.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论