Understanding variable scope in Go

They're making the same point, with the same rules, about two different things: the first is about variables and constants, the second is about type identifiers. So, if you declare a type inside a block, the same scoping rules apply as would apply to a variable declared at the same spot.


Besides applying to different things (rule #5 is for constant- and variable declarations, rule #6 is for type declarations), there is also an important difference in wording:

  1. The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.
  2. The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.

This is the reason why there are 2 rules and not one.

What does this mean? What does the difference imply?

# 5 Variable and Constant declarations (inside a function)

The scope of the declared variables or constants begins at the end of the declaration. This means if you're creating a function variable, initializing it with an anonymous function, it can't refer to itself.

This is invalid:

f := func() {
    f()
}

Attempting to compile:

prog.go:5:3: undefined: f

This is because the declaration ends after the closing bracket of the anonymous function, so inside of it you can't call f(). A workaround would be:

var f func()
f = func() {
    f()
}

Now here the declaration of f ends at the closing parenthesis (of its type func()), so in the next line when we assign an anonymous function to it, it is valid to refer to f (to call the function value stored in the f variable) because it is now in scope. See related question: Define a recursive function within a function in Go

Similarly, when initializing a variable e.g. with a composite literal, you can't refer to the variable inside of it:

var m = map[int]string{
    1:  "one",
    21: "twenty-" + m[1],
}

This gives a compile-time error ("undefined: m"), because m is not in scope yet inside the composite literal.

And obviously this workaround works:

var m = map[int]string{
    1: "one",
}
m[21] = "twenty-" + m[1]

# 6 Type declarations (inside a function)

The scope of the declared type begins at the identifier in the declaration. So in contrast to rule #5, it is valid to refer to the type itself inside its declaration.

Does it have any advantage / significance?

Yes, you can declare recursive types, such as this:

type Node struct {
    Left, Right *Node
}

The type identifier Node is in scope right after it appears in the type declaration, which ends with the closing bracket, but before that we could refer to it, meaningfully.

Another example would be a slice type whose element type is itself:

type Foo []Foo

You can read more about it here: How can a slice contain itself?

Yet another valid example:

type M map[int]M