How to return a value in a Go function that panics?

huangapple go评论84阅读模式
英文:

How to return a value in a Go function that panics?

问题

我的Go函数预期返回一个值,但在调用库函数时可能会发生恐慌。我可以使用recover()在延迟调用中捕获这个恐慌,但在这种情况下如何返回一个值呢?

func MyFunc() string {
    defer func() {
        if err := recover(); err != nil {
            // 在恐慌发生时如何使MyFunc()返回一个值?
        }
    }()
    SomeFuncThatMayPanic()
    return "正常返回值"
    // 在恐慌发生时如何返回"错误"?
}
英文:

My Go function is expected to return a value, but it may panic when calling a library function. I can use recover() to capture this in a deferred call, but how can I return a value in this case?

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

得分: 56

你可以使用命名返回参数。给你的返回值命名,在检测到 panic 时,在延迟函数中可以更改返回值的“变量”。更改后的新值将被返回。

示例:

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

这在规范:延迟语句中有提到:

例如,如果延迟函数是一个函数字面量,并且周围的函数具有在字面量内部范围内的命名返回参数,则延迟函数可以访问和修改返回参数,然后再返回它们。

在博客文章延迟、恐慌和恢复中也提到了这一点:

延迟函数可以读取和赋值给返回函数的命名返回值。

还在Effective Go:恢复中提到:

如果 doParse 发生 panic,恢复块将把返回值设置为 nil,延迟函数可以修改命名返回值。

英文:

You can use named result parameters. Name your return values, and in the deferred function when panic was detected, you can change the values of the return "variables". The changed new values will be returned.

Example:

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"
}

Output (try it on the Go Playground):

Returned: was panic, recovered value: test

This is mentioned in the Spec: Defer statements:

> For instance, if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.

It is also mentioned in the blog post Defer, Panic and Recover:

> Deferred functions may read and assign to the returning function's named return values.

And also in Effective Go: Recover:

> If doParse panics, the recovery block will set the return value to nil—deferred functions can modify named return values.

答案2

得分: 2

使用icza的答案中的示例,如果你想知道如果从panic中恢复但不为命名返回值赋值会发生什么,就像这样:

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

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

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

Go Playground中运行示例

英文:

Using the example in icza's answer, and if you wonder what happens if you recover from the panic but don't assign the value for the named return, like this:

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

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

The function will return the zero value of the specified return type.

Run the example in the Go Playground

答案3

得分: 1

你可以像这样使用命名参数:

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

func MyFunc() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = ConvertRecoverToError(r)
        }
    }()
    panic("test")
    return nil
}

func ConvertRecoverToError(r interface{}) error {
    switch x := r.(type) {
    case string:
        return errors.New(x)
    case error:
        return x
    default:
        return errors.New(fmt.Sprint(x))
    }
}

请注意,我已经将代码中的"替换为了双引号"

英文:

You can use named parameter like this:

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

func MyFunc() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err=convertRecoverToError(r)
        }
    }()
    panic("test")
    return nil
}

func ConvertRecoverToError(r interface{}) error {
	switch x := r.(type) {
	case string:
		return errors.New(x)
	case error:
		return x
	default:
		return errors.New(fmt.Sprint(x))
	}
}

huangapple
  • 本文由 发表于 2015年10月16日 17:45:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/33167282.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定