Skip to content

Instantly share code, notes, and snippets.

@chmike
Last active August 3, 2020 09:23
Show Gist options
  • Save chmike/ac0113afefbc04e67323b4a3688d6b54 to your computer and use it in GitHub Desktop.
Save chmike/ac0113afefbc04e67323b4a3688d6b54 to your computer and use it in GitHub Desktop.

Problems with Go's shorthand declaration :=

The shadow variable trap

The problem

It is best illustrated with the following Go code snippet.

var x int
x, y := 1, 2 // x declared above is set to 1, y is declared and set to 2
if true {
  x, z := 3, 4 // declare a local x and z, and set them to 3 and 4. This x shadows the x declared above.
}

The trap is that the x in the if blocks is a new local variable that shadows the x variable declared in the enclosing block.

The solutions

  1. While constraining, the use of := should not allow predefied variables in the lefthand side of :=. This would remove any ambiguity. Unfortunately, this is not backward compatible and might break a lot of code. It is however my preferred solution because it removes all ambiguity on variable declaration and shadowing. It would be a short hand of var x, y = 1, 2.

  2. A compiler warning should be issued when a variable on the lefthand side of := might shadow a variable defined in the enclosing context. It would be backward compatible. But a lot of existing code would issue compiilation warnings.

  3. Dissalow the := notation. I had to suggest it for completeness, but this would break even more code and piss off a lot of people. Don't even think of it. ;)

:= can't be used with member fields

The problem

It is best illustrated with the following Go code snippet.

var x
x, y := 1, 2 // ok
var a struct{b int}
a.b, c := 1, 2 // compilation error: "expected identifier on left side of :=" or "non-name a.b on left side of :="

This appears inconsistent. Why is x allowed and not a.b ? Both are predefined variables. Even the compilation error message appears inconsistent. With the above code, the compilation error message is "expected identifier on left side of :=". If we remove the var x and x, y := 1, 2 lines, the compilation error message becomes "non-name a.b on left side of :=".

The solutions

  1. Disallow use of any predefined variables on the lefthand side of :=. Any varible on the lefthand side of := would be declared at that spot. It's my preferred solution. See comment above.

  2. Allow use of a.b as any other predefined variables. There is no risk of shadow variable trap with this change. It would be backward compatible.

Note: published in "Golang experience report"

@deefdragon
Copy link

I encountered this problem for the first time when dealing with a function that returned an error and a value that was to be set as global. While the specific problem may be solved by upcoming error changes, it still exists as a problem for multi returns.

I feel that something that needs to be considered as a solution to the shadowing problem would be to disallow shadowing completely. As far as I am aware, it is bad practice to have shadowing in code to begin with as it is easy to create confusion on what variable is being used. Though it would have a problem with backwards compatibility, most problem code would benefit from having different variable names in different scopes.

@floppyzedolfin
Copy link

I encounter this issue quite a lot, especially when affecting values to members of structs.
var err error a.b, err = myFunc()
I think the first line is excessive.

Same goes when checking if a value is in an array, and affecting it to a member.
var ok bool a.b, ok = myMap[key]

I would happily use a new operator, ?= that will first try to affect a value to a variable, if it exists, and then create a variable with that value.
a.b, ok ?= myMap[key]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment