Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
kidlj opened this issue Oct 26, 2020 · 3 comments
Closed

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

kidlj opened this issue Oct 26, 2020 · 3 comments

Comments

@kidlj
Copy link

@kidlj kidlj commented Oct 26, 2020

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 }.
@gopherbot gopherbot added this to the Proposal milestone Oct 26, 2020
@gopherbot gopherbot added the Proposal label Oct 26, 2020
@ianlancetaylor ianlancetaylor changed the title proposal: add '?' and '!' built-in function names for error handling proposal: Go 2: add '?' and '!' built-in function names for error handling Oct 26, 2020
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 26, 2020

See #40432. Similar to #25273 and #32884.

@kidlj
Copy link
Author

@kidlj kidlj commented Oct 26, 2020

See #40432. Similar to #25273 and #32884.

Thank you. Sorry for not checking that meta issue first.

@kidlj
Copy link
Author

@kidlj kidlj commented Oct 27, 2020

Also, there must be concurrency safety problems with this approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.