Some related posts I've found:
- go variable scope and shadowing
- Golang: variable scope inside if statements
- Limit the scope of variables storing error
Also there are many use cases to Variable Scoping and Shadowing.
Any code samples or answers will be appreciated.
Variable scoping and shadowing:
Go is lexically scoped using blocks:
- The scope of a predeclared identifier is the universe block.
- The scope of an identifier denoting a constant, type, variable, or
function (but not method) declared at top level (outside any function)
is the package block.
- The scope of the package name of an imported
package is the file block of the file containing the import
declaration.
- The scope of an identifier denoting a method
receiver, function parameter, or result variable is the function body.
- 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.
- 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.
An identifier declared in a block may be redeclared in an inner block.
While the identifier of the inner declaration is in scope, it
denotes the entity declared by the inner declaration.
The package clause is not a declaration; the package name does not
appear in any scope. Its purpose is to identify the files belonging to
the same package and to specify the default package name for import
declarations.
Advantages:
- Since data cannot be accessed from outer scope, Data Integrity is preserved
Different forms of Variable shadowing in Go:
Golang way to limit variable scope (using short-hand assignment inside statements):
package main
import "fmt"
func main() {
i := 10 //scope: main
j := 4
for i := 'a'; i < 'b'; i++ {
// i shadowed inside this block
fmt.Println(i, j) //97 4
}
fmt.Println(i, j) //10 4
if i := "test"; len(i) == j {
// i shadowed inside this block
fmt.Println(i, j) // i= test , j= 4
} else {
// i shadowed inside this block
fmt.Println(i, j) //test 40
}
fmt.Println(i, j) //10 4
}
When "we need more alphabets", this is nice way to limit variables scope.
Also this works well when you need more local variables or scope:
using {
and }
pair:
Pros: no need to extra statements like if, for, …
package main
import "fmt"
func main() {
i := 1
j := 2
//new scope :
{
i := "hi" //new local var
j++
fmt.Println(i, j) //hi 3
}
fmt.Println(i, j) //1 3
}
Another way to limit variable scope is using function calls:
Pros: scope limit, input value type parameters are usable like local variables,
Cons: call/return time, and stack usage: if it is not optimized by compiler
package main
import "fmt"
func fun(i int, j *int) {
i++ //+nice: use as local var without side effect
*j++ //+nice: intentionally use as global var
fmt.Println(i, *j) //11 21
}
func main() {
i := 10 //scope: main
j := 20
fun(i, &j)
fmt.Println(i, j) //10 21
}
Another way is shadowing global variables:
package main
import "fmt"
var i int = 1 //global
func main() {
j := 2
fmt.Println(i, j) //1 2
i := 10 //Shadowing global var
fmt.Println(i, j) //10 2
fun(i, j) //10 2
}
func fun(i, j int) {
//i := 100 //error: no new variables on left side of :=
//var i int = 100 //error: i redeclared in this block
fmt.Println(i, j) //10 2
}
See: Variable shadowing and Scope.
And: Declarations and scope: