如何在恐慌的 Go 函数中返回一个值?

Posted

技术标签:

【中文标题】如何在恐慌的 Go 函数中返回一个值?【英文标题】:How to return a value in a Go function that panics? 【发布时间】:2016-01-15 00:25:46 【问题描述】:

我的 Go 函数应该返回一个值,但是在调用库函数时它可能会出现恐慌。我可以使用recover() 在延迟调用中捕获它,但在这种情况下如何返回值?

func MyFunc() string
    defer func() 
        if err := recover(); err != nil 
            // What do I do to make MyFunc() return a value in case of panic?
        
    ()
    SomeFuncThatMayPanic()
    return "Normal Return Value"
    // How can I return "ERROR" in case of panic?

【问题讨论】:

【参考方案1】:

使用 icza 的答案中的示例,如果您想知道 如果您从恐慌中恢复但不为命名的 return 分配值会发生什么,如下所示:

func main() 
    fmt.Println("Returned:", MyFunc()) // false


func MyFunc() (ret bool) 
    defer func() 
        if r := recover(); r != nil 
        
    ()
    panic("test")
    return true

函数将返回指定返回类型的零值

运行Go Playground中的示例

【讨论】:

【参考方案2】:

您可以使用named result parameters。命名您的返回值,并在检测到恐慌时的延迟函数中,您可以更改返回“变量”的值。将返回更改后的新值。

例子:

func main() 
    fmt.Println("Returned:", MyFunc())


func MyFunc() (ret string) 
    defer func() 
        if r := recover(); r != nil 
            ret = fmt.Sprintf("was panic, recovered value: %v", r)
        
    ()
    panic("test")
    return "Normal Return Value"

输出(在Go Playground上试试):

Returned: was panic, recovered value: test

Spec: Defer statements中提到了这个:

例如,如果延迟函数是 function literal 并且周围的函数有 named result parameters 在字面量内,则延迟函数可以在返回结果参数之前访问和修改结果参数。

博文Defer, Panic and Recover中也提到过:

延迟函数可以读取并分配给返回函数的命名返回值。

还有Effective Go: Recover:

如果doParse 发生紧急情况,恢复块会将返回值设置为nil——延迟函数可以修改命名的返回值。

【讨论】:

我对如果没有命名返回值会返回什么感兴趣。玩你的playground,好像是type的零值 @KyleChadha 是的,如果结果未命名并且未达到return 语句(例如由于恐慌),则返回结果类型的零值。这在Why can a normal return hide a panic that a named return correctly provides to the caller?中有详细说明

以上是关于如何在恐慌的 Go 函数中返回一个值?的主要内容,如果未能解决你的问题,请参考以下文章

go中defer的理解--deferreturn返回值之间执行顺序

Go MySQL 中的错误:func (*Rows) Scan 返回恐慌:(func() string)

恐慌:json:无法将数组解组为 main.Structure 类型的 Go 值

如何找到Go标准库函数的可能错误值?

Golang入门到项目实战 golang 函数

21 | panic函数recover函数以及defer语句 (上)