Description
Preface
There have been quite a few proposals for more concise error handling in go, and to be honest, a lot of them don't really fit with the 'go style' - admittedly that's a very subjective claim so I'll leave that up to others to decide relative to this proposal.
I've noticed in a couple places that Go already has set a precedence for using comments in code with semantic meaning.
Similarly being used by some linting/static analysis tools (eg https://golangci-lint.run/usage/false-positives/#nolint)
This proposal is for simple error handling and wrapping to follow a similar pattern
Example
Here's a very simple example that you might currently see in go code
func foo(x string) (string, error) {
return "", fmt.Errorf("some error occured :(")
}
func bar1(x string) (string, error) {
y, err := foo(x)
if err != nil {
return "", fmt.Errorf("could not call foo: %w", err)
}
return y, nil
}
and here is my proposed pattern:
func bar2(x string) (string, error) {
// error: fmt.Errorf("could not call foo: %w", err)
y := foo(x)
return y, nil
}
equivalent form using a specified identifier name:
func bar3(x string) (string, error) {
y := foo(x) // error: errValue => fmt.Errorf("could not call foo: %w", errValue)
return y, nil
}
Backwards Compatibility
Since the code suggested will currently not compile (assignment mismatch: 1 variable but foo returns 2 values
), this should be backwards compatible with go1
Non features
Any error handling beyond wrapping and returning are not supported. I believe those situations are unique enough where the current solutions are not overly verbose and will not benefit from any unique new syntax
Breakdown
The error comment will work either on the statement directly following, or apply to the current statement if added on the same line
func someFunctionThatErrors(arguments) (type1, type2, error) {
// error: identifier => expression(identifier)
a, b := anotherFunctionThatErrors(arguments)
// ...
}
or
func someFunctionThatErrors(arguments) (type1, type2, error) {
a, b := anotherFunctionThatErrors(arguments) // error: identifier => expression(identifier)
// ...
}
will be pre-processed into
func someFunctionThatErrors(arguments) (type1, type2, error) {
a, b, identifier := anotherFunctionThatErrors(arguments)
if identifier != nil {
return ZeroValue, ZeroValue, expression(identifier)
}
// ...
}
The identifier =>
part of the error comment can be skipped, defaulting to the err
identifier instead
The expression(identifier)
doesn't need to be a function, or even depend on the identifier in any way. It's just an expression that can be returned as an error. For instance,
// Wrap the error in a new error type
// error: ErrSomeThing { data: "foobar", err: err }
// For returning no error but exiting early
// error: nil
Final thoughts
I'm not suggesting this is the best solution, but one that I haven't seen discussed anywhere and thought it would be worth proposing. It's not the first proposal based on just error wrapping though. Most of those I have seen add new operators to the language. Go is not very operator heavy so they often feel out of place in the language, which is where I think this proposal fits better