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.