Understanding variable scope in Go

前端 未结 2 1612
梦谈多话
梦谈多话 2021-01-12 10:06

I am going through the Go specification to learn the language, and these points are taken from the spec under Declarations and scope.

Though I am able t

2条回答
  •  执笔经年
    2021-01-12 10:21

    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
    

提交回复
热议问题