The :=
syntax is error-prone when dealing with multiple targets and variables of enclosing scope:
// often a mistake:
var a int
{
a, b := foo() // creates a new 'a' in this scope instead of using 'a' of outer scope
}
// the fix
var a int
{
var b *Bar
a, b = foo()
}
Not only is the fix verbose, it defeats a nicety of inferred typing that it often spares us from having to remember precise return type(s), e.g. whether a returned type is a pointer or not:
a := bar() // whether bar() returns an *X or just an X, we often don't need to care
Normally, Go disallows redeclarations, but it makes a hacky exception for the :=
syntax, such that we can't look at an :=
assignment and know for sure which of the target variables are being declared:
x, y, z := foo() // only one of x, y, and z must be new, but just looking here doesn't tell us which variables are new
Another problem is that we can't mix 'name' targets and 'non-name' targets:
a, b[0] := foo() // compile error: b[0] is a non-name target
Instead of marking an assignment itself as an implicit declaration, mark the individual target variables. Let's say for now we'll use '
as a suffix marker:
x', y, z' = foo() // assign to new variable 'x', existing variable 'y', and new variable 'z'
With this syntax, I don't see a good reason to disallow mixing in non-name targets:
a', b[0] = foo() // ok
It would also be nice if we could optionally specify types of one or more of the newly declared target variables to make them different from the corresponding return type:
a' Fruit, b = bar() // first return type of bar() is Banana, but we store it in a Fruit interface variable
Lastly, while it's great that Go requires explicit casts in most cases, it would sometimes be nice to cast the individual return values in a multi-assignment. In fact, it would sometimes be nice if we could perform any arbitrary operation on each return value. A solution would be to allow an expression after each target where _
stands in for the return value:
// the first return value is cast to int64 and the result assigned to new variable 'a'
// the second return value is added to 3 and the result assigned to existing variable 'b'
a' int64(_), b _ + 3 = foo()
If arbitrary expressions in these positions make the code too cluttered, perhaps restrict the expressions to being just single function/method calls.
Another small thing I'd like in Go is a literal that stands-in for any zero value. Let's say #
is this special literal:
var f Foo // struct type Foo
f = # // f = Foo{}
This literal would be particularly handy when returning zero values along with a non-nil error. Instead of having to write specific zero values...
return SomeStruct{}, "", err
...we could just write:
return #, #, err
Hi Brian, your proposal makes sense, but imagine how many repos this change will break,
:=
it is so widely used. Or you are thinking to have both options:=
as well as'
??I'm sure Go dev team is thinking on enhancements for version 2 that do not affect version 1 code.