This is a creation in Article, where the information may have evolved or changed.
The scope of a variable is the extent to which the variable can be used effectively in program code. Do not mix scopes and lifetimes. A scope is an area of code, a compile-time attribute, and a lifetime is the period during which a variable survives a program, during which time the variable can be referenced by other parts of the program and is the concept of the runtime.
A syntax block is a series of statements contained within curly braces, such as a function body or a loop body. Variables declared inside a syntax block cannot be accessed by external code of the syntax block. We can extend the concept of local syntax blocks, which in some scenarios do not require curly braces, which is called lexical chunks. Lexical blocks are divided into several types: global lexical blocks, including all source code, packet lexical blocks, including the entire package, the file lexical block containing the entire file, the lexical block for the for, if, switch statement, the lexical block of the case branch in switch or select, and of course, the previous mentioned syntax blocks .
The lexical block of a declaration statement determines the scope of the variable. The built-in type of Go language, built-in functions, built-in constants are global lexical blocks, so they are global scope, such as int, Len, true, etc., can be used directly throughout the program, for imported packages, such as Temconv imported FMT package, is the file lexical block, Therefore, the FMT package can only be accessed in the current file, where FMT is the scope of the full file range; Tempconv. The variable C in the Ctof function is a local lexical block (syntax block), so its scope is inside the function.
The label (label) behind the control statement, such as the label after break, continue, or Goto, whose scope is inside the function where the control statement resides.
A program may have multiple identical variable names, as long as they are declared in a different lexical block. For example, you can declare a local variable x within a function and then declare a variable x at the package level, which is inside the function, where the local variable x is substituted for the latter, which is called shadow, meaning that the local variable hides the package variable within the scope of the function.
When the compiler encounters a reference to a variable name, it searches for the variable's declaration statement, starting with the inner lexical block, and then until the global lexical block. If the declaration statement for the variable name cannot be found at compile time, an error will be made: undeclared name. If the variable name is declared at the same time in the internal lexical block and the external lexical block, then the internal lexical block is found first based on the search rule of the compilation period. In this case, the internal declaration hides the external declaration (shadow), at which point the externally declared variable is inaccessible:
Func f () {}var g = "G" Func Main () { F: = "F" FMT. Println (f)//"F"; The local variable F hides the packet-level function f fmt. PRINTLN (g)//"G"; Packet-level variable G fmt. Println (h)//Compile Error:undefined:h}
in theInside a function, lexical blocks can nest any number of layers, so local variables can hide external variables. Most of the syntax blocks (curly braces) are created by controlling the statement if, for, and so on, and the following program has three different x variables, each of which is declared in a different lexical block (this code is mainly to illustrate the scope of the rules, this coding style is not advocated!). ):
Func Main () { x: = "hello!" For I: = 0; I < Len (x); i++ { x: = X[i] if x! = '! ' { x: = x + ' A '-' a ' fmt. Printf ("%c", X)//"HELLO" (one letter per iteration)}}}
The expression X[i] and X + ' a '-' a ' respectively refer to different x variables, which are explained later.
As mentioned earlier, not all lexical blocks have explicit curly braces. The For loop above creates two lexical blocks: a loop body with curly braces, an explicit lexical block, and an implicit lexical block without curly braces, such as declaring a variable i in a For loop conditional statement. The scope of I here contains the for conditional statement and for body.
The following example also creates three variable x, each declared in a different lexical block, one in the body of the function, one in the implicit lexical block for the for, and one in the explicit lexical block-loop body, where only 1 and 3 are explicit lexical blocks:
Func Main () { x: = ' Hello ' for _, x: = range x { x: = x + ' A '-' a ' fmt. Printf ("%c", X)//"HELLO" (one character per loop) }}
just likeFor loops, an if statement and a switch statement create an implicit lexical block. The following code illustrates the scope of x and y in the If-else chain:
if x: = f (); x = = 0 { fmt. PRINTLN (x)} else if y: = g (x); x = = y { fmt. Println (x, y)} else { fmt. Println (x, y)}fmt. Println (x, y)//compile error:x and Y is not visible here
The second if statement is nested inside the first, so a variable declared in the first if statement is visible to the second if statement. There are similar rules in switch: Each case has its own lexical block in addition to the conditional lexical block.
For a package-level variable, the order and scope of the declaration are irrelevant, and all a package-level variable declaration can refer to it itself or the package-level variable declared after it, however, if a variable or constant references itself at the time of declaration, the compiler will error.
Look at the following program:
If f, err: = OS. Open (fname); Err! = Nil {//compile error:unused:f return err}f.readbyte ()//compile error:undefined ff. Close () //compile error:undefined F
The scope of f is only the IF statement, so the lexical block outside of if is inaccessible, and a compile error is reported.
Here you can change the code and declare the F variable in advance:
F, err: = OS. Open (fname) if err! = Nil { return err}f.readbyte () F.close ()
If you do not want to declare variables in an external lexical block, you can write:
If f, err: = OS. Open (fname); Err! = Nil { return err} else { //F and err is visible here too f.readbyte () f.close ()}
But the third is not the go recommendation, the second is more appropriate, separating normal logic from error handling.
The scope of the short declaration variable is to pay particular attention to, consider the following program, the beginning will get the current working directory, saved in a package-level variable. This could have been done by invoking the OS in the main function. GETWD is done, but separating this logic from the main logic with the INIT function is a better choice, especially since the operation to get the directory might fail, which requires handling the returned error. function log. Fatalf prints a message and then calls the OS. Exit (1) Finalization procedure:
var cwd stringfunc init () { CWD, err: = OS. GETWD ()//compile ERROR:UNUSED:CWD if err! = Nil { log. Fatalf ("OS. GETWD failed:%v ", Err) }}
Here both CWD and err are not declared in the lexical block of Init, so: the = statement declares both of them as local variables. The CWD declaration inside Init is hidden from the outside, so the program does not meet the purpose of updating the package-level variable CWD.
In the current version, the Go compiler detects that the local variable CWD has never been used and therefore will error, but this check is not very strict, for example, if the log. The value of the CWD is printed in Fatalf (then the local variable CWD is used), then this error will be hidden!!!
var cwd stringfunc init () { CWD, err: = OS. GETWD ()//Note here The package level CWD is hidden locally, but the compiler did not error! If err! = Nil { log. Fatalf ("OS. GETWD failed:%v ", err) } log. Printf ("Working directory =%s", CWD)}
The global variable CWD is not properly initialized, and the log function uses the CWD local variable to hide the bug.
There are ways to handle this potential error, the most straightforward is to avoid using: =, declare the ERR variable with var:
var cwd stringfunc init () { var err error CWD, err = os. GETWD () if err! = Nil { log. Fatalf ("OS. GETWD failed:%v ", Err) }}
In this chapter, we simply learn about packages, files, statements, statements, and so on. In the next two chapters, we'll learn about data Structures .
PS. This chapter is really hard to write, it took 3 hours. As a quality comparison you can see this article scope.