Hi everyone! this is Jimmy , and this is the third article in my series “Breaking Things with Go.” In this series, I document my journey through Jon Bodner’s Second Edition: Learning Go – An Idiomatic Approach to Real-World Go Programming and explore how to use Go in the most practical way I can
in this series the resources are the book itself, go documentation, and any AI model to clarify some things
lets Jump into it

enough with the composite types already (I’m done believe me) so lets talk about something else for a change (or maybe cause the chapter is already done) who knows ??
this chapter will be mainly about the Control Structure but we can’t go through if statements and loops and goto statements without explaining the Blocks and Shadowing first so we would pay attention to it later
Blocks and Shadowing
just like most programming languages blocks exist
- all variables defined at the top level of a function are in block
- with in a function each braces defines another block
- control statements are also blocks on their own
you can access identifiers defined in outer block within any inner block but now the other way around
Shadowing
the variable shadowing happens when you declare an identifier in an outer block and re-declare it under the same name in an inner block
for example
name := "Dan"
if name == "Dan" {
fmt.Println(name) // Dan
name := "alex"
fmt.Println(name) // alex
}
fmt.Println(name) // Dan
as long as the shadowing variable exists (meaning we are still in the same block after shadowing) → no way to access the shadowed variable "Dan"
- and the shadowing ends where the close braces found then you access the original value again
the shadowing happens by re-declaring so you have to use either var or := but using just equal operator in the inner block is just reassigning and it will affect the actual value
x := 10
if x < 15{
fmt.Println(x) //10
x = 12
fmt.Println(x) //12
}
fmt.Println(x) //12
that's why we said using
:=for declaration isn't safe cause it can make it unclear what variables are being used and we can shadow one of them by accident
you have to make sure you don't shadow package import like this for example
package main
import "fmt"
func main(){
x := 10
fmt.Println(x) // print 10 normally
fmt := "oops" // this shadows the package named fmt in the file block
fmt.Println(fmt) // gets error cause the declared variable `fmt` doesn't have field called Println
}
the problem isn't the definition of fmt as variable but is trying to access non-existing field called Println so how that happens ? isn't fmt some kind of keyword that i can't use ? no it isn't a keyword
The Universe Block
go has 25 keywords only and guess what ? int, string, true, false, make, close, nil aren't included in the list they are pre-declared identifiers and they are defined in what's known as the universe block (the block which contains all other blocks)
because they declared in the universe block (which is outer block from main block perspective) we can shadow them without any issue for example
fmt.Println(true) // prints true boolean value
true := 10
fmt.Println(true) // 10
so don't use them as identifiers names cause they will cause bugs and strange behavior and what is worse is they will be very hard to track
If Statement
- in go we don't put the condition in parentheses
- any variable declared within the brace of an if or else statement exists only within that block
- we can declare variables that are scoped to the condition and to both if and else blocks
if variables declaration; condition {
#execute if true
} else if another condition {
#execute if true
} else{
#execute if others not true
}
as you can see you can declare variables while writing if statement
- but you can declare them using
:=only var isn't allowed - declaring variables under the same name in that place still considered shadowing cause this is a new block now
- but you can declare them using
the else if and else block has to be on the same line as the closing braces of the previous block
if n := rand.Intn(10); n == 0 { // n is accessible only through the if, else if, else blocks
fmt.Println(n)
} else if n > 5 {
fmt.Println("number is higher than 5")
} else {
fmt.Println("good number")
}
fmt.Println(n) // compiling error undeclared name
Takes
- Blocks define scope so variables are only accessible within the block they’re declared in but inner blocks can read outer blocks
- Shadowing is powerful but dangerous re-declaring a variable using
:=orvarin an inner block hides the outer one, which can easily lead to bugs especially when shadowing imports or pre-declared identifiers (from the universe block) ifstatements create their own scope, variables declared in anifcondition exist only within theif / else if / elsechain, and using the same name there still counts as shadowing
Coming Next
the next break will be about for loops and maybe switch statements (based on the content length don’t wanna make it too long to read)
so stick around for next break where we will break more stuff
feel free to reach out to me
