Skip to content

proposal: Go 2: add '?' and '!' built-in function names for error handling #42214

Closed
@kidlj

Description

@kidlj

I'm proposing adding two built-in functions ? and ! for better error handling.

? and ! are built-in function names, by default they refer to two functions in errors package.

? = errors.Log
! = errors.Return

package errors

func Log(err error) {
        log.Printf("%v\n", err)
}

func Return(err error) error {
          return err
}

You can see the difference from their definitions, that errors.Log simply logs the error and errors.Return returns a new error.

We use ? or ! where err should place, normally as the last return value receiver from a function call, and they receive the err value.

func GetUser(name string) (*User, error) {
    user, err := getUser(name)
    if err != nil {
        return nil, err
    }
    age, err := getAge(name)
    // Just log the error
    if err != nil {
       log.Printf("%v\n", err)
    }
    user.Age = age
    return user, nil
}

can become:

func GetUser(name string) (*User, error) {
    // If the err value that ! receives is not nil, produce a new error and this function returns here.
    user, ! := getUser(name)
    // if the err value that ? receives is not nil, logs the error and this function goes on.
    age, ? := getAge(name)
    user.Age = age
   return user, nil
}

What ! does here is to receive the last return value(the err) from getUser(name), and if err != nil, execute errors.Return with err as its sole argument and produce a new err, then return the outside function GetUser with its last return value(error type) being the new err and other return values as what they are. If the value ! receives is nil, nothing will happen.

The difference between ? and ! is that if the err value ? receives is not nil, it just executes errors.Log for logging but GetUser goes on, it won't return here.

We use ? and ! to mark that ! will return the outside function if the err value it receives is not nil and ? won't.

Customization

Since ? and ! are built-in function names, they can be assigned to other error handling functions at package or function level.

In many cases we want to return a new error that wraps error contexts. We can do that with !.

package errors

func WrapReturn(format string, args ...interface{}) func(error) error {
  return func(err error) error {
     return fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err)
  }
}

We now use errors.WrapReturn to return an error handling function and assign it to ! at any time:

func GetUser(name string) (*User, error) {
    // This is an assignment statement, we need a little work to make `!` a valid function name.
    // To distinguish ! assignment and ! call below, we may need to change ! call to `!()`(for discussion).
    ! = errors.WrapReturn("GetUser (name=%s)", name)
    user, ! := getUser(name)
    age, ? := getAge(name)
    user.Age = age
   return user, nil
}

As you can see, we can assign ! or ? to our customized error handling functions at package level or function level, at any time. So you can name a package level error handling strategy and override it in a specific function.

Summary

  1. This is not a formal Go proposal, I'm not capable of doing that. This is just a new idea for discussion.(Sorry for the proposal title.)

  2. This approach won't meet all error handling scenarios. But it do has some advantages:

    • Compatible with if err != nil common practice, you can combine both when ? or ! is not sufficient.
    • Can mitigate many if err != nil { return err } cases. This is its first goal.
    • Very customizable. You can name your project or package or function level error handling strategy, and inherit or override the larger scope strategy at any time.
    • Keeps the number of value receivers from a function call same as the number of the called function return values.
    • Can distinguish clearly whether an error will cause early return, just like using if err != nil { return err }.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeLanguageChangeSuggested changes to the Go languageProposalerror-handlingLanguage & library change proposals that are about error handling.v2An incompatible library change

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions