Skip to content

proposal: spec: new builtin: handle #69045

Closed as not planned
Closed as not planned
@jimmyfrasche

Description

@jimmyfrasche

What

handle must be deferred and only in a function whose last return value is type error. For brevity, I'll refer to this value as "the error" of the function.

handle takes an argument of type assignable to func(error) error. For brevity, I'll refer to this argument as an "error transformer".

If the error is nil, the error transformer is not called. If the error is not nil, the error transformer is called and its result replaces the error. Essentially:

if err != nil {
  err = transform(err)
}

Why?

This allows error handling routines to be written as library code. For example

func wrap(msg string) func(error) error {
  msg += ": %w"
  return func(err error) error {
    return fmt.Errorf(msg, err)
  }
}

which can be used like

defer handle(wrap("additional context"))

How?

This can be done via a macro expansion like mechanism in the compiler.

Given

func example() error {
  defer handle(wrap("oops"))
  //...
}

the compiler can treat it as if the code was

func example() (the_error error) {
  defer handle_impl(&the_error, wrap("oops"))
  //...
}
func handle_impl(err *error, f func(error) error) {
  if *err != nil {
    *err = f(*err)
  }
}

Alternatives

This could just be a regular function like:

package errors
func Handle(err *error, f func(error) error) { //...

That has two downsides:

  1. the invocation is even longer
  2. it's up to the user to pass the correct error which requires naming the return when otherwise unnecessary and making sure that name doesn't get shadowed.

A builtin obviates these, making it easier to use and thus more likely to be used.

This could be a keyword like

handle func(err error) error {
  return fmt.Errorf("oops: %w", err)
}

which is obviously much better but not backwards compatible.

A func returnedError() *error builtin that returns a pointer to the error of the deferring function. This is more powerful. You could use it to write handle and other things. It's a bit too magical and harder to use.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions