From Zero to Go Hero: Learn Go Programming with Me - Part 3
Understanding Pointers, defer, switch

Introduction
Welcome to Part 3 of the Learning Go with Me series. I hope you like it so far and learned from it. Because that’s the most important thing. If you have any doubts or comments please feel free to comment it. I will try to help you out.
In the last article, we learned lots of fundamental concepts of Go. And in this blog, I decided to just focus on the Pointers.
Let’s start without further ado
What is a Pointer?
A pointer is nothing but a variable that stores the “memory address” of another variable. What does that mean?
So instead of holding a direct value which we typically do like
age := 24, the pointer holds the address of where that value is stored in memory.You can think of pointers as post office boxes.

- Each box points to a certain address/ house number. Similarly, if you have defined a variable like this:
num := 42
- Then you can point to this variable using another variable with a pointer
ptr := &num
- You need to first understand what ‘ * ‘ and ‘ & ’ lexical elements mean.
Address Operator (&):
- The & operator is used to get the memory address of a variable. If
xis an integer variable then&xwill give you pointers toxthat is, a memory address where the integerxis stored.
- The & operator is used to get the memory address of a variable. If
Dereferencing Operator ( * ):
- The * operator is used to get the value stored at a memory address. If
pis a pointer to an integer, then*pwill give you the integer thatppoints to.
- The * operator is used to get the value stored at a memory address. If
Example:
func main(){
num := 42
ptr:= &num
fmt.Println(ptr) // 0xc000104040 - Address where num is stored
fmt.Println(*ptr) // 43 - Value at which ptr is pointing
}
- In this example,
ptris a pointer tonum. You can get the value ofnumby dereferencingptrwith*ptr.
Passing Pointer to Functions
Pointers also come into play when you're dealing with functions.
If you pass a variable to a function in Go, the function gets a copy of the variable. Everything in go is a pass-by value, if the function changes the variable, it won't affect the original. But if you pass a pointer to a variable, the function can dereference the pointer to change the original variable.
Let’s take an example
// Without Pointer - Pass by Value
func valueSemantic(num int){
num = 43
}
func main(){
num := 42
fmt.Println("Before: ", num) // 42
valueSemantic(num)
fmt.Println("After: ", num) // 42
}
- This is because a copy of num is getting passed to the function
valueSemanticand not the actual value. To update the value that we are passing from other functions we have to use a pointer.
// With pointer - Pass by Value
func valueSemantic(num *int){
*num = 43
}
func main(){
num := 42
fmt.Println("Before: ", num) // 42
valueSemantic(&num)
fmt.Println("After: ", num) // 43
}
As you can see now it got changed. Why?
Because now we are not passing just the copy of the
numbut we are passing it an address wherenumis stored.And in the function parameter now that we are passing an address to it we need to accept that address using a pointer. Because remember? The pointer points to another variable’s address.
Inside the function, we can’t just do this and change the variable:
num = 43
- Because
numis a pointer. So first we need to get the value of that pointer by dereferencing it like this*numand then assign a new value to it.
*num = 43
Pointers with Structs
Lastly, pointers are crucial when dealing with structs in Go. Since Go doesn’t have classes
and objects, you can use structs to create complex types, and you can use pointers to structs to modify these structs or to avoid copying the structs around.
Let’s take an example:
type Person struct {
name string
age int
}
func updateName(p *Person, newName string) {
p.name = newName // Modify the struct via pointer
}
func main() {
p := Person{name: "Alice", age: 30}
fmt.Println("Before:", p.name) // Prints: Alice
updateName(&p, "Bob")
fmt.Println("After:", p.name) // Prints: Bob
}
We aren’t doing anything new in this. We created a struct of type Person. It has
nameandagefields.And inside
mainfunction we are passing the address of this personpinupdateNamefunction.updateNametakes two parameters. Pointer to a struct person and new name.But inside the function, you see we didn’t use
*to deference it. Why?This is because Go is automatically dereferencing that struct for us. So when you have a pointer to a struct in Go, the language provides a convenience called automatic dereferencing for accessing fields or calling methods. You don't need to explicitly dereference the pointer using
*before accessing fields or methods.
For Primitive Types (e.g., int, float, etc.): You must use
*to dereference the pointer and access the value.For Structs: Go automatically dereferences pointers to structs when you access their fields or call methods.
- In my previous blog, I forgot two concepts to discuss which are
deferandswitch casestatements. Let’s understand them one by one
defer
In Go, the
deferthe keyword is used to ensure that a function call is executed later in a program’s execution, usually for the purposes of cleanup.deferis often used whereensureandfinallywould be used in other languages.When a function call is deferred, it's placed onto a stack. The function calls on the stack are executed when the surrounding function returns, not when the
deferthe statement is called.They're executed in Last In, First Out (LIFO) order, so if you have multiple
deferstatements, the most recently deferred function will be the first one to execute when the function returns.
func main() {
fmt.Println("Start")
defer fmt.Println("This will print last")
fmt.Println("End")
}
Output:
Start
End
This will print last
Multiple defer Statements
func main() {
defer fmt.Println("First")
defer fmt.Println("Second")
defer fmt.Println("Third")
}
Output
Third
Second
First
// LIFO order as explained above
Real-World Example: Closing a File
func main() {
file, err := os.Open("example.txt") // Opening a file
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close() // Ensure the file is closed before exiting the function
// Perform file operations here...
fmt.Println("File opened successfully")
}
In real-world programming, we have many things open. We need to make sure we close those types of functions because otherwise, it will create memory leakage. Although Go has a Garbage collector that will dump those things down if not used, but it’s always a good practice to close the instance of it once the work is done.
And that’s where
defercan be used. We use defer right after we perform operations like opening a file to make sure we don’t forget.
Switch
switchin Go is a powerful way to control the flow of execution by matching a value against multiple cases. It is simpler and cleaner than writing multipleif-elseconditions.
How switch works
switchevaluates a value and executes the first matchingcase.If no
casematches, thedefaultblock if executed.
Example
func main() {
day := "Monday"
switch day {
case "Monday":
fmt.Println("Start of the workweek!")
case "Friday":
fmt.Println("Almost the weekend!")
default:
fmt.Println("Just another day.")
}
}
In the above example, the
switchmatchesdaytoMondayand executes the corresponding blockIn Go we can use Switch without an Expression. So If we omit the expression, Go evaluates the first
casethat istrue
func main() {
x := 15
switch {
case x < 10:
fmt.Println("Less than 10")
case x < 20:
fmt.Println("Between 10 and 20") // Prints this
default:
fmt.Println("20 or more")
}
}
Fallthrough in switch
- The
fallthroughkeyword forces the execution of the next case, even if the condition is nottrue
func main() {
num := 2
switch num {
case 1:
fmt.Println("One")
case 2:
fmt.Println("Two")
fallthrough
case 3:
fmt.Println("Three")
default:
fmt.Println("Other")
}
}
/*
Output:
Two
Three
*/
Wrapping Up
In this article, we explored some of the fundamental concepts of Go, including pointers, defer statements, and switch statements.
We learned that pointers store the memory address of variables, enabling functions to modify original data without creating copies.
The
deferstatement schedules function calls to execute after the surrounding function completes.The
switchstatement offers a good control flow based on variable values.I think this is pretty much it for you to know to get started. So from the next article, we will start building out Book CRUD API. There are still a few things remaining to explain and I will be explaining on the go as it will make much more sense when we are building something.
I hope you have learned something from this article. If you have any feedback or queries, feel free to ask them in the comments. I’ll be happy to answer all your questions
See you in the next article, until then….

- Connect with me on Twitter, LinkedIn, and Github.





