英文:
Go: returning from defer
问题
我想要在一个函数中,如果发生 panic 的话返回一个错误(在 Go 语言中):
func getReport(filename string) (rep report, err error) {
rep.data = make(map[string]float64)
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
err, _ = r.(error)
return nil, err
}
}()
panic("Report format not recognized.")
// 其他 getReport 函数的代码,可能会尝试越界访问一个切片
...
}
我似乎对 panic 和 defer 的概念有所误解。有人可以给我解释一下吗?
英文:
I want to return an error from a function if it panics (in Go):
func getReport(filename string) (rep report, err error) {
rep.data = make(map[string]float64)
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
err, _ = r.(error)
return nil, err
}
}()
panic("Report format not recognized.")
// rest of the getReport function, which can try to out-of-bound-access a slice
...
}
I appear to have misunderstood the very concept of panic and defer. Can anybody enlighten me?
答案1
得分: 81
在延迟函数中,你可以修改返回的参数,但不能返回一个新的集合。所以只需对你的代码进行简单修改就可以使其正常工作。
你所写的代码还有另一个问题,即你使用了string
类型进行恐慌处理,但在类型断言中却期望一个error
类型。
以下是修复这两个问题的代码(Play链接):
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
// 确定错误的具体类型并设置err
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("未知的恐慌")
}
// 使rep无效
rep = nil
// 返回修改后的err和rep
}
}()
英文:
In a deferred function you can alter the returned parameters, but you can't return a new set. So a simple change to what you have will make it work.
There is another problem with what you wrote, namely that the you've paniced with a string
but are expecting an error
in your type assertion.
Here is a fix for both of those (Play)
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
// find out exactly what the error was and set err
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("Unknown panic")
}
// invalidate rep
rep = nil
// return the modified err and rep
}
}()
答案2
得分: 7
请看这个代码:
package main
import "fmt"
func iWillPanic() {
panic("ops, panic")
}
func runner() (rtnValue string) {
rtnValue = ""
defer func() {
if r := recover(); r != nil {
// 在这里记录日志或者其他操作,不要什么都不记录
rtnValue = "不要慌" // 修改返回值,然后返回
}
}()
iWillPanic()
return rtnValue
}
func main() {
fmt.Println("返回值:", runner())
}
这段代码的功能是调用iWillPanic
函数,该函数会引发一个恐慌(panic)。在runner
函数中,使用defer
语句和recover
函数来捕获恐慌,并在恐慌发生时修改返回值。最后,在main
函数中打印返回值。
注意:在恐慌发生时,程序会立即停止执行,并且不会执行后续的代码。
英文:
have a look at this
package main
import "fmt"
func iWillPanic() {
panic("ops, panic")
}
func runner() (rtnValue string) {
rtnValue := ""
defer func() {
if r := recover(); r != nil {
// and your logs or something here, log nothing with panic is not a good idea
rtnValue = "don't panic" // modify the return value, and it will return
}
}()
iWillPanic()
return rtnValue
}
func main() {
fmt.Println("Return Value:", runner())
}
答案3
得分: 3
func TestReturnFromPanic(t *testing.T) {
fn := func(filename string) (rep string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("在getReport中发生panic:%s", r)
}
}()
return filename[100:], nil
}
t.Log(fn(""))
}
命名返回参数err
是关键。
https://play.golang.org/p/jpaCa9j2iAf
英文:
func TestReturnFromPanic(t *testing.T) {
fn := func(filename string) (rep string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic in getReport %s", r)
}
}()
return filename[100:], nil
}
t.Log(fn(``))
}
The named return parameter err
is the trick.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论