头图

练习5.19: 使用panic和recover编写一个不包含return语句但能返回一个非零值的函数。

搜到以下答案,对照着进行了一些思考。

package main

import "fmt"

func main() {
    a := noReturn()
    fmt.Println(a)
}

func noReturn() (result int) {
    defer func() {
        p := recover()
        result = p.(int)
    }()
    panic(42)
}

首先观察一下下面代码:

func main() {    
    s := deferDemo3()
    fmt.Printf("%T %q", s, s) // string "set in defer"
}


func deferDemo3() (s string) {
    defer func() {
        s = "set in defer" // 可以在defer调用中对返回值进行修改
    }()
    return "on return"
}

从输出结果可以看到,我们可以在defer调用中对命名返回值进行修改。因此,再观察下面代码:

func main() {
    s := causePanic()
    fmt.Printf("%T %q", s, s) // string ""
}

func causePanic() string {
    defer func() {
        if p := recover(); p != nil {
            fmt.Printf("recover from panic: %s\n", p)
        }
    }()
    panic("oops!")
    return "after panic" // 永远不会执行到
}

输出结果为:

recover from panic: oops!
string ""

虽然函数causePanic的return语句因为panic导致永远无法达到,但是由于函数定义了返回值,且自动被初始化为字符串零值,所以main函数中变量s依然获得了返回值""

因此,如果返回值是命名返回值(返回值有名字),则可以在panic终止了函数causePanic继续运行后,同样可以在defer调用中对返回值进行修改,前提是必须是返回值必须有名字。

func main() {
    s := causePanic()
    fmt.Printf("%T %q", s, s) // string "set in defer"
}

func causePanic() (s string) {
    defer func() {
        if p := recover(); p != nil {
            fmt.Printf("recover from panic: %s\n", p)
      s = "set in defer" // 在defer中对返回值s进行修改
        }
    }()
    panic("oops!")
    return "after panic" // 永远不会执行到
}

输出结果为:

recover from panic: oops!
string "set in defer"

NeosqiCb
19 声望0 粉丝