Demystifying Go Variables: A Practical Guide

Faizan
4 min readFeb 24, 2024

--

In this quick guide, we’ll journey through the fundamentals of Go variables with practical examples, enabling you to harness their power effectively.

1.Declaring Variables: In Go, variables can be explicitly and implicitly declared using walrus operator (:=) declared. We can also have “Compound Creation”, “Block Creation”.

Most important thing here is you must use declared variables. Compiler will throw error for unused variables.

package main

import "fmt"

func main() {

//implicit declaration
name := "John Doe"

//explicit declaration
var age int
age = 30

// compound creation
var a, b, c = 1, 2, "compound"

// block creation
var (
d int = 3
e int = 4
f = "block"
)

// compound can also be done using walrus operator
g, h, i := 5, 6, "walrus compound"

fmt.Println("Name:", name)
fmt.Println("Age:", age)
fmt.Printf("Print compound : %v %v %v \n", a, b, c)
fmt.Printf("Print block : %v %v %v \n", d, e, f)
fmt.Printf("Print walrus compound : %v %v %v \n", g, h, i)
}

2.Immutable and Mutable Variables: Go supports both immutable and. mutable variables. Immutable variables are declared using the ‘const’ keyword, while mutable variables are declared using ‘var’.

package main

import "fmt"

func main() {
const pi = 3.14 // immutable
var radius = 5 // mutable
fmt.Println("Area:", pi*float64(radius*radius))
}

3.Scope and Lifetime: Variables in Go have different scopes — package-level, function-level, or block-level.

package main

import "fmt"
var globalVar = 100 // package-level variable
func main() {
var localVar = 50 // function-level variable
{
var blockVar = 10 // block-level variable
fmt.Println("Block variable:", blockVar)
}
fmt.Println("Local variable:", localVar)
fmt.Println("Global variable:", globalVar)
}

Explanation:

  • globalVar is a package-level variable, declared outside any function. It can be accessed from any file within the same package.
  • localVar is a function-level variable, declared within the main function. It is only accessible within the main function and cannot be accessed from outside.
  • blockVar is a block-level variable, declared within a block enclosed by curly braces {} inside the main function. It is only accessible within that block and cannot be accessed from outside or from within the main function.

4.Concurrency and Goroutines: Variables in Goroutines can be shared among concurrent processes, requiring synchronization to maintain data integrity.

package main

import (
"fmt"
"sync"
)

var counter = 0

func incrementCounter(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
counter++
}
}

func main() {
var wg sync.WaitGroup
wg.Add(2)
go incrementCounter(&wg)
go incrementCounter(&wg)
wg.Wait()
fmt.Println("Counter:", counter)
}

Explanation:

  • The counter variable is declared globally to be accessed and modified by multiple Goroutines.
  • The incrementCounter function is defined to increment the counter in a loop for 1000 iterations. It receives a pointer to a sync.WaitGroup as an argument. The defer wg.Done() statement ensures that wg.Done() is called when the function exits, effectively decrementing the WaitGroup counter once the function completes its execution.
  • In the main function, a sync.WaitGroup named wg is created to synchronize the Goroutines. It's incremented by 2 using wg.Add(2) to indicate that there are two Goroutines that need to be waited for.
  • Two Goroutines are launched concurrently using the go keyword, each executing the incrementCounter function with a pointer to the wg WaitGroup.
  • wg.Wait() blocks until the WaitGroup counter becomes 0, indicating that both Goroutines have completed their execution.
  • Finally, the value of the counter variable is printed, showing the total number of increments performed by both Goroutines.

5.Pointers and How to use them: A pointer is a variable that stores the memory address of another variable. It allows indirect access to the value of the variable it points to. Understanding pointers is crucial for efficient memory management and passing references to data structures in functions. Let’s delve into pointers with an example:

package main

import "fmt"

func main() {
// Declare a variable
var num int = 42

// Declare a pointer variable
var ptr *int

// Assign the address of 'num' to 'ptr'
ptr = &num

// Accessing the value using the pointer
fmt.Println("Value of num:", num)
fmt.Println("Address of num:", &num)
fmt.Println("Value of num via pointer:", *ptr)
fmt.Println("Address stored in pointer:", ptr)

// Modifying the value using the pointer
*ptr = 100
fmt.Println("Modified value of num via pointer:", num)
}

Explanation:

  • We declare a variable num of type int and assign it the value 42.
  • We declare a pointer variable ptr of type *int. The asterisk * denotes that ptr is a pointer.
  • We assign the address of the num variable to the pointer ptr using the & operator. This operation is called "taking the address of" or "referencing".
  • We print the value of num, the address of num, the value of num accessed via the pointer (*ptr), and the address stored in the pointer.
  • We modify the value of num indirectly through the pointer by assigning 100 to *ptr. This operation is called "dereferencing".

Conclusion: With these practical examples, you’ve embarked on a journey through the essence of Go variables. Whether you’re building simple scripts or complex applications, mastering variables is essential for crafting robust and efficient Go programs. Keep exploring, keep coding, and let the power of Go variables drive your innovations forward!

--

--