←back to thread

Go subtleties

(harrisoncramer.me)
235 points darccio | 4 comments | | HN request time: 0.559s | source
1. callc ◴[] No.45670316[source]
I had a “wtf” moment when using Go around panic() and recover()

I was so surprised by the design choice to need to put recover in in deferred function calls. It’s crazy to smush together the error handling and normal execution code.

replies(2): >>45670956 #>>45672793 #
2. lelandbatey ◴[] No.45670956[source]
It's cause it's not normal error handling to use recover(). In smaller codebases, panic probably should not be present. For larger codebases, recover should be in place only in very very sparse locations (e.g. at the top level http handler middleware to catch panics caused by unreliable code). But in general, returning errors is supposed to be how all errors are signaled. I've always loved the semantic distinction between panics vs errors in go, they feel sooo much clearer than "normal" exception handling (try ... catch) in other languages which syntactically equivocate such common cases as "this file doesn't exist" with "the program is misbehaving due to physical RAM corruption". I think it's great that panic vs errors makes that a brighter line.

Assuming recover has to exist, I think forcing it to be in a deferred function is genius because it composes so well with how defers work in go. It's guaranteed to run "when the function returns" which is exactly the time to catch such truly catastrophic behaviors.

replies(1): >>45673844 #
3. gethly ◴[] No.45672793[source]
Semantics. Go at least does not restrict you to wrap every single panicky call.

func Foo() { try { maybePanic() } catch (err any) { doSomething(err) }

  .. more code
}

vs

func Foo() { defer func() { if err := recover(); err != nil { doSomething(err) } }()

  maybePanic()

  .. more code
}
4. ignoramous ◴[] No.45673844[source]
Recover() has its sharp edges. If some downstream function part of the execution stack does not release a critical resource because its execution was halted midway by the bubbling panic, then the resource will leak (and in the case of a locked sync.Mutex, subsequent Lock()s will deadlock).

Until go1.23 [0], Recover() comes in handy for fault reports, however; ex: https://github.com/hashicorp/terraform/blob/325d18262e/inter...

[0] which introduced debug.SetCrashOutput: https://pkg.go.dev/runtime/debug#SetCrashOutput