Go: Proposal: Assigning to fields with short declaration notation

Created on 28 Nov 2013  路  15Comments  路  Source: golang/go

Is there any good reason for this to be unsupported:

    x.f, err := f()

This would be unambiguous, as it would only work if x was previously declared and err
was not.

The fact it doesn't work forces the use of:

    var err error
    x.f, err = f()

Which while okay, doesn't feel like an improvement over the former version.

As is known, there's also precedence for the use of := on reassignments, given that the
statement:

    x, err := f()

may reassign x if it was already declared in the current scope.
FrozenDueToAge Go2 LanguageChange Proposal

Most helpful comment

@gkop You will have to declare that error variable somewhere; e.g.:

var err error
s.f, err = f()
if err ...

All 15 comments

Comment 1:

The := notation is a shorthand for common cases. It's not meant to cover every possible
declaration one may write. I'd prefer to leave as is, but won't close this until others
have weighed in.

_Labels changed: added priority-someday, languagechange, removed priority-triage._

_Status changed to Thinking._

Comment 2:

I was squinting at something like Gustavo's first example in a readability review
recently, actually wondering if it should work. I think it should work and can't think
of any drawbacks to the change, apart from complicating the definition of :=.

Comment 3:

In the first level, := was a syntactic sugar for a handy case
        var v T           // (1)
        v = exprOfTypeT
so it can be written as just
        v := exprOfTypeT  // (2)
(1) and (2) relate in a simple way. Per this proposal, it would do also allow
        var v t // (3)
        w.x, v = exprOfTypeOfFieldXFromStructW, exprOfTypeT
Shortening the explicit form (3) to
        w.x, v := exprOfTypeOfFieldXFromStructW, exprOfTypeT // (4)
means that (3) and (4) are no more related in such a simple way, IMO rendering the
original sweetness toxic. Consider (by extension)
        *p, v := f()
        s[x], x := g() 
        h(i(j())).z[a]().b, y := k(), x // etc.
I don't think this improves readability. It's much easier to miss the fact that {v,x,y}
are being declared because of the distraction of the now possibly unbound complexity of
the LHSs. In contrast to the clear meaning of the status quo dead simple LHSs legal with
:=
        a, b, c := f()
        d, e, f := expr1, expr2, expr3.

Comment 4:

_Labels changed: added release-none, removed go1.3maybe._

Comment 5:

_Labels changed: added repo-main._

Comment 6:

_Owner changed to @griesemer._

A slightly different case is when it happens in the if statement:
if x.f, ok := f(); !ok {
You usually want that ok variable visible only inside that if statement, and you don't want to declare it in outer scope.

It seems like the drawback to this change is in the error, or not ok, code path.

When a function returns a value or an error, sometimes that value is nil, other times it is just the zero value.

When you are just assigning to variables, this is fine. However, once you start allowing struct properties or array indices to also be on the LHS, you open yourself to the (likely) possibility of assigning a new value into your struct/array during an error case, which is suboptimal at best.

(I'm a newb who found this behavior surprising) Is the current best practice to forward-declare err and use =, or use a temporary local variable for the struct field? I suppose "it depends". Is there a good article on the subject?

@gkop It's common to write code such as:

res1, err := f1(...)
if err != nil ...
res2, err := f2(...)
if err != nil ...

and re-use the same error variable. Sometimes the results can be local, so one might write

if res, err := f(...); err == nil {
   // use res
} else {
   // handle err, etc
}

It's rarely necessary to "forward-declare" an error variable (usually only if the other variables are not new and need to be used and there's no error variable yet - again, this is very rare).

@griesemer thanks! I'm particularly interested, what about when one of the variables we're assigning is a struct field and err isn't yet declared?

@gkop You will have to declare that error variable somewhere; e.g.:

var err error
s.f, err = f()
if err ...

Changed this into a proposal so we can collect feedback and eventually make a decision.

Let's leave this with #377 for now and revisit if and when we get to Go 2.

This bug has reappeared as #30318.

Was this page helpful?
0 / 5 - 0 ratings