Description
(The following content is a rewritten and an improvement version of this comment)
Abstract
This proposal tries to avoid some problems caused by short variable declarations (a.k.a., variable redeclarations), by introducing a scope viewer identifier concept and using labels as scope viewer identifiers.
Background
We all know that, although short variable declarations (x, y := a, b
) do solve some problems, they also brings some new ones. The new problems include:
- break the one way to do a specified thing principle in Go. We all know that short declarations overlap too much with standard declarations in functionalities.
- short declarations brings some confusions to Go programmers, in particular to new gophers.
- It is not very clear to get which identifiers are new declared and which are redeclared at the first glance.
- Almost every gophers ever dropped in the famous variable shadowing trap caused by using short declarations. A real world example: Update negotiator.go Azure/go-ntlmssp#24, and an older issue.
- Not all L-values suitable to act as target values are allowed to show up as the target values in short declarations. proposal: spec: let := support any l-value that = supports #30318. (Another similar one.)
- The original intention of short variable declarations is to make code less verbose. However, it makes code more verbose in several scenarios.
Proposal
Proposal part 1: scope viewers
The proposal proposes that we can re-use labels as scope viewer identifiers. The following code shows what are scope viewer identifiers:
func f() {
var a = 1
x:
{
y:
var a = 2
z:
{
var a = 3
// x:a <=> y:a
println(x:a, y:a, z:a, a) // 1 1 2 3
}
}
}
Proposal part 2: syntax sugars
There are two sugars for the scope:identifier
notation.
:identifier
(without the scope prefix), means the innermost declaredidentifier
(it is equivalent to*&identifier
).::identifier
, means the package-level declaredidentifier
.
(Digression 1: is it good to view package import names as scope viewer identifiers? So that we can use aPkg::ExportIdentifier
to use exported resources.)
(Digression 2: the proposal may also discard the above described scope viewer concept and only contain the following content, by replacing :identifier
with *&identifier
and using *&identifier
as redeclared items.)
Proposal part 3: avoid using short declarations by using scope viewer identifiers
The proposal proposes that we can relax the restriction of the left items in standard variable declarations. Now all left items must be pure identifiers. We can relax the rules as selectors, dereferences, element indexing, and the above mentioned scope:identifier
forms are allowed to show up as target values in the left items of a standard variable declaration, as long as there is at least one pure identifier in the left items as well. When the declaration is executed at run time, the new variables represented by pure identifiers are initialized, others are re-assigned with new values. The following is an example without using labels:
type T struct {
i *int
s []int
}
var t T
var p = new(int)
func bar() {
// "err" is new declared, others are not.
var *p, t.i, t.s[1], :p, err = 1, p, 2, t.i, errors.New("bar")
// *:p is not valid. In other words, :id must show up independently.
...
}
Another more common use case:
package bar
func foo() {
var x, err = f()
...
// Here "err" means the "err" declared above.
var y, z, :err = g()
s:
...
{
// The "err" also means the "err" declared above.
var w, :err = h()
...
// This "err" is a new declared one.
var m, n, err = k()
...
{
// This "err" means the first error declared above.
var p, q, s:err = l()
...
}
}
}
The :=
redeclaration syntax should be kept ONLY in the simple statements in control flows, just for aesthetics reason. In other words, ... := ...
is totally a shorthand of var ... = ...
in the simple statements in control flows.
if a, b := c, d; a {
}
//<=>
if var a, b = c, d; a { // some ugly
}
// However, personally, I also think the ugly version is acceptable.
Rationale
Please see the above backgroud part.
Compatibility
The idea should be Go 1 compatible. What I mean here is the above proposed new redeclaration syntax may coexist with the current standard and short variable declarations.
It is easy to let go fix
translate short variable declarations into the above proposed new syntax form. The short declarations in the simple statements in control flows don't need to be translated.
If it needs, the current short declaration uses can be removed eventually.
Implementation
Compiler parsers should consider the new syntax form.
One new statement might need to be added to go/ast
standard package.