Skip to content

proposal: Go 2: support "assign if nil" statement to tackle error handling boilerplate #21732

Closed
@faiface

Description

@faiface

Hello,

it seems to me that when people complain about Go's error handling, they mostly complain about the situations when it gets repetitive (otherwise I assume it's not a big problem), like this:

a, b, err := f1()
if err != nil {
    return nil, errors.Wrap(err, "failed")
}
c, err := f2(a)
if err != nil {
    return nil, errors.Wrap(err, "failed")
}
d, err := f3(b, c)
if err != nil {
    return nil, errors.Wrap(err, "failed")
}

Now, I usually don't encounter code like this, but I agree that this looks awful and is hard to prevent sometimes.

So I came up with a simple solution. Keep in mind, the solution is just an idea which might be iterated upon, or completely thrown away.

Assign if nil statement

Here's my solution.

In an assignment statement (= or :=), when any LHS operand is surrounded by parenthesis, the assignment will evaluate and assign if and only if all parenthesis-surrounded LHS operands are zero value (nil or an appropriate equivalent)

In case of := assignment, variables undeclared before are treated as zero valued.

What I mean is basically that this

a, b, (err) := f1()

gets translated to this

// assume a, b and err are declared
if err == nil {
    a, b, err = f1()
}

In general, something like this

a, b, (c), (d) := f()

gets translated to

if c == nil && d == nil {
    a, b, c, d = f()
}

How does this help?

With "assign if nil" statement, we can rewrite the original error handling chain like this:

a, b, (err) := f1()
c, (err) := f2(a)
d, (err) := f3(b, c)
if err != nil {
    return nil, errors.Wrap(err, "failed")
}

In the first line, err is undeclared and thus is handled as if it was nil and f1 executes. Next line only executes f2 and assigns the values if err is still nil, i.e. when the previous call finished without errors. The same holds for the third line.

In the last line, we nicely wrap the error with additional context information and return it.

Other uses

There are more uses of the "assign if nil" statement. For example, lazily initializing a map:

func (s *Struct) Method() {
    (s.m) = make(map[string]string) // this only gets executed if s.m is nil
}

Summary

I propose introducing an "assign if nil" statement. Proper formal specification is not done here, but is easy to imagine.

I think this construct solves most of the hassle with error handling in Go. An if err != nil once a while is perfectly fine, the only problem is when there is too many of them. I believe that's almost always possible to avoid using this construct.

Looking forward to your feedback!

Michal Štrba

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