Handling errors that can arise in imperative languages is difficult. This is partially because errors can arise during error handling as well. Rather than allow more ways to write imperative error handling, we should make error handling more declarative and more decoupled than the imperative code that triggers it.
Many types of error handling are variations on a few themes: close something, delete something, or notify something.
This sounds a lot like defer
. Why hasn't defer
been enough? Partially because defer
only works for resources and operations whose lifetime is scoped to the current function.
In practice many error conditions cause us to "manually unwind" execution of the current goroutine by returning early from the current function using the familiar if err != nil { ...
idiom. What if we had a way to "abort" the entire goroutine and execute any "deferred" operations as we aborted? In the extreme, each deferred item might even be executed in its own goroutine.
An approach like this would ruffle some feathers. It implies we should construct our applications a bit differently (many more goroutines). It forces a bit more rigor around resources and error conditions (which goroutine "owns" this resource?). Finally, it highlights a need for thorough treatment of a goroutine's lifecycle.
If you're left feeling skeptical, consider the simplicity of the operating system's resource management and error handling: when your process exits (or aborts) all its (now unused) file handles, sockets and locks are automatically closed. The declarative and deterministic nature of these cleanup policies mean that's relatively rare that the exit (or force kill) of a process yields system-wide instability.
Modern operating systems handle these scenarios so well that it's taken for granted that killing an errant process can make a system more stable.
We might expect just as much from Golang's own error handling.