Repository: go
Issue: proposal: spec: reduce noise in return statements that contain mostly zero values
Labels: LanguageChange,Proposal,error-handling,dotdotdot,LanguageChangeReview

Update: the current proposal is to permit `return ..., v` to return the zero value for all but the last result, and to return `v` as the last result.  The most common use will be `return ..., err`. 

A variant under discussion include `return ..., v1, v2` to return zeroes for all but the last N.

Another variant is to permit `return ...` to return zeroes for all.

In general `...` is only permitted if it omits one or more values--`func F() err { return ..., errors.New("") }` is not permitted.

---

# Proposal

In return statements, allow `...` to signify, roughly, "and everything else is the zero value". It can replace one or more zero values.

This is best described by example:

Given the function signature `func() (int, string, *T, Struct, error)`:

`return 0, "", nil, Struct{}, nil` may be written `return ...`

`return 0, "", nil, Struct{}, err` may be written `return ..., err`

`return 0, "", nil, Struct{X: Y}, nil` may be written `return ..., Struct{X: Y}, nil`

`return 1, "", nil, Struct{}, nil` may be written `return 1, ...`

`return 1, "a", nil, Struct{}, nil` may be written `return 1, "a", ...`

`return 1, "", nil, Struct{}, err` may be written `return 1, ..., err`

`return 1, "a", nil, Struct{X: Y}, err` may be written `return 1, "a", ..., Struct{X: Y}, err`

The following is invalid:

`return ..., Struct{X: Y}, ...` — there can be at most one `...` in a return statement

# Rationale

It is common for a function with multiple return values to return only one non-zero result when returning early due to errors.

This creates several annoyances of varying degrees.

When writing the code one or more zero values must be manually specified. This is at best a minor annoyance and not worth a language change.

Editing the code after changing the type of, removing one of, or adding another return value is quite annoying but the compiler is fast enough and helpful enough to largely mitigate this.

For both of the above external tooling can help: https://github.com/sqs/goreturns

However, the unsolved problem and motivation for the proposal is that it is quite annoying to read code like this. When reading `return 0, "", nil, Struct{}, err` unnecessary time is spent pattern matching the majority of the return values with the various zero value forms. The only signal, `err`, is pushed off to the side. The same intent is coded more explicitly and more directly with `return ..., err`. Additionally, the previous two minor annoyances go away with this more explicit form.

# History

This is a generalized version of a suggestion made by @nigeltao in https://github.com/golang/go/issues/19642#issuecomment-288559297 where #19642 was a proposal to allow a single token, `_`, to be sugar for the zero value of any type.

I revived the notion in https://github.com/golang/go/issues/21161#issuecomment-317891763 where #21161 is the currently latest proposal to simplify the `if err != nil { return err }` boilerplate.

# Discussion

This can be handled entirely with the naked return, but that has greater readability issues, can lead too easily to returning the wrong or partially constructed values, and is generally (and correctly) frowned upon in all but the simplest of cases.

Having a universal zero value, like `_` reduces the need to recognize individual entries as a zero value greatly improving the readability, but is still somewhat noisy as it must encode n zero values in the common case of `return _, _, _, _, err`. It is a more general proposal but, outside of returns, the use cases for a universal zero value largely only help with the case of a non-pointer struct literal. I believe the correct way to deal that is to increase the contexts in which the type of a struct literal may be elided as described in #12854

In https://github.com/golang/go/issues/19642#issuecomment-288644162 @rogpeppe suggested the following workaround:
```
func f() (int, string, *T, Struct, error) {
  fail := func(err error) (int, string, *T, Struct, error) {
    return 0, "", nil, Struct{}, err
  }
  // ...
  if err != nil {
    return fail(err)
  }
  // ...
}
```

This has the benefit of introducing nothing new to the language. It reduces the annoyances caused by writing and editing the return values by creating a single place to write/edit the return values. It helps a lot with the reading but still has some boilerplate to read and take in. However, this pattern could be sufficient.

This proposal would complicate the grammar for the return statement and hence the go/ast package so it is not backwards compatible in the strict Go 1 sense, but as the construction is currently illegal and undefined it is compatible in the the Go 2 sense.

# Possible restrictions

Only allow a single value other than `...`. 

Only allow it on the left (`return ..., err`). 

Do not allow it in the middle (`return 1, ..., err`).

While these restrictions are likely how it would be used in practice anyway, I don't see a need for the limitations. If it proves troublesome in practice it could be flagged by a linter.

# Possible generalizations

Allow it to replace zero or more values making the below legal:
```
func f() error {
  // ...
  if err != nil {
    return ..., err
  }
  // ...
}
```

This would allow easier writing and editing but hurt the readability by implying that there were other possible returns. It would also make this non-sequitur legal: `func noop() { return ... }`. It does not seem worth it.

Allow `...` in assignments. This would allow resetting of many variables to zero at once like `a, b, c = ...` (but not `var a, b, c = ...` or `a, b, c := ...` as their is no type to deduce) . In this case I believe the explicitness of the zero values is more a boon than an impediment. This is also far less common in actual code than a return returning multiple zero values.