Skip to content

proposal: spec: require return values to be explicitly used or ignored (Go 2) #20803

Open
@bcmills

Description

@bcmills

Today, if a Go function returns both a value and an error, the user must either assign the error to a variable

v, err := computeTheThing()

or explicitly ignore it

v, _ := computeTheThing()

However, errors that are the only return value (as from io.Closer.Close or proto.Unmarshal) or paired with an often-unneeded return value (as from io.Writer.Write) are easy to accidentally forget, and not obvious in the code when forgotten.

tx.Commit()  // Store the transaction in the database!

The same problem can occur even when errors are not involved, as in functional-style APIs:

t := time.Now()
t.Add(10 * time.Second)  // Should be t = t.Add(…)
strconv.AppendQuote(dst, "suffix")  // Should be dst = strconv.AppendQuote(…)

For the few cases where the user really does intend to ignore the return-values, it's easy to make that explicit in the code:

_, _ = fmt.Fprintln(&buf, v)  // Writes to a bytes.Buffer cannot fail.

go func() { _, _ = fmt.Fprintln(os.Stderr, findTheBug()) }()

And that transformation should be straightforward to apply to an existing code base (e.g. in a Go 1-to-2 conversion).

On the other hand, the consequences of a forgotten error-check or a dropped assignment can be quite severe (e.g., corrupted entries stored to a production database, silent failure to commit user data to long-term storage, crashes due to unvalidated user inputs).


Other modern languages with explicit error propagation avoid this problem by requiring (or allowing API authors to require) return-values to be used.

  • Swift warns about unused return values, but allows the warning to be suppressed if the @discardableResult attribute is set.
    • Prior to a change in Swift 3, return-values could be ignored by default (but a warning could be added with @warn_unused_result)
  • Rust has the #[must_use] attribute.
  • C++17 has the [[nodiscard]] attribute, which standardizes the longstanding __attribute__((warn_unused_result)) GNU extension.
  • The ghc Haskell compiler provides warning flags for unused results (-fwarn-unused-do-bind and -fwarn-wrong-do-bind).
  • OCaml warns about unused return values by default. It provides an ignore function in the standard library.

I believe that the OCaml approach in particular would mesh well with the existing design of the Go language.

I propose that Go should reject unused return-values by default.

If we do so, we may also want to consider an ignore built-in or keyword to ignore any number of values:

go func() { ignore(fmt.Fprintln(os.Stderr, findTheBug())) }()

or

go func() { ignore fmt.Fprintln(os.Stderr, findTheBug()) }()

or

go func() { _ fmt.Fprintln(os.Stderr, findTheBug()) }()

If we use a built-in function, we would probably want a corresponding vet check to avoid subtle eager-evaluation bugs:

go ignore(fmt.Fprintln(os.Stderr, findTheBug()))  // BUG: evaluated immediately

Related proposals
Extending vet checks for dropped errors: #19727, #20148
Making the language more strict about unused variables in general: #20802
Changing error-propagation: #19991

Metadata

Metadata

Assignees

No one assigned

    Labels

    LanguageChangeSuggested changes to the Go languageLanguageChangeReviewDiscussed by language change review committeeProposalerror-handlingLanguage & library change proposals that are about error handling.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions