Skip to content

proposal: Go 2: spec: simple error wrapper handling using comments #47934

Closed
@conradludgate

Description

@conradludgate

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.

  1. https://go.dev/blog/examples
  2. https://go.dev/blog/generate

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions