Scope of Variables

In programming, the scope of a variable refers to the part of the program where that variable is accessible. Understanding this concept is crucial because it determines where you can use or modify a variable. There are two main types of scope in most languages: global scope and local scope.
Global Scope
When a variable is declared outside of any function or block, it is said to have global scope. Such variables are accessible from anywhere in the program, across all functions. They are often referred to as having package scope since they are available across all source files within a package.
Local Scope
A variable declared inside a function only exists within that function, which means it has local scope. This also applies to function parameters and return values—they can only be accessed inside the function they belong to. In essence, a local variable’s scope is restricted to the block where it is declared.
To visualize this, consider a code block enclosed in { }. The variables declared inside that block belong to a local scope and can't be accessed outside of it.
Working Example of Variable Scope
Let's say you have a variable named number declared at the top of your program, outside any function. Since it exists outside all functions, it has a global scope and can be accessed and modified from anywhere in the program.
var number = 5
fmt.Println(number) // Output: 5
number = 10
fmt.Println(number) // Output: 10
Here, number is initialized with 5, and when printed, it outputs 5. We then update it to 10, and on printing again, the output reflects the new value. Global variables like this can be modified from any function or part of the code.
However, if you declare a variable inside a function, it's restricted to that function. You can even declare a new variable with the same name inside a nested block, which will shadow the outer variable. Be cautious with shadowing, as it can sometimes lead to subtle bugs in your code.
Printing in Go
So far, you've likely used fmt.Println() to print outputs to the console. Another handy function is fmt.Printf(), which gives more control over formatting. It uses a format string with specific placeholders (or format specifiers) to define how the output should appear.
fmt.Printf("The value of number is: %d\n", number)
In this case:
%dis the format specifier for integers.\nis for a newline.
Some common format specifiers are:
%dfor integers%sfor strings%vfor any general value
Using Printf helps when you need more customized output, especially when dealing with different types of variables.
Value Types and Reference Types
Memory in a computer is like a collection of "boxes" called words, which can store data. Depending on your system, a word is typically 32 bits (4 bytes) or 64 bits (8 bytes). Variables are stored in these words, and each has a unique memory address.
Value Types
In languages like Go, variables of basic types such as int, float, bool, and string are value types. When you assign one value type to another, the actual value is copied. For instance:
i := 10
j := i // j gets a copy of the value of i
In this case, j holds its own copy of the value of i. Changing i later won’t affect j, because they are separate copies.
Value Type Example (int):
i = 10 j = i (copy value of i)
┌────────┐ ┌────────┐
│ 10 │ │ 10 │
└────────┘ └────────┘
(stack) (stack)
i and j hold independent copies of 10
Reference Types
More complex data structures like slices, maps, or structs are reference types. These variables store a reference (or memory address) that points to the actual data in memory. When you assign one reference type to another, only the reference is copied—not the actual data.
r1 := someStruct{}
r2 := r1 // r2 now holds the same reference as r1
Now, both r1 and r2 refer to the same memory location. Modifying r1 will also reflect in r2, as they both point to the same data.
Reference Type Example (slice, struct, etc.):
r1 = [1, 2, 3] r2 = r1 (copy reference of r1)
┌────────┐ ┌────────┐
│ │ │ │
│ ref: ──┼───┐ │ ref: ──┼───┐
└────────┘ │ └────────┘ │
▼ ▼
┌───────────────────────┐
│ [1, 2, 3] │
└───────────────────────┘
(heap memory)
r1 and r2 point to the same data in heap memory
Stack vs. Heap Memory
Value types are generally stored in the stack—a smaller but faster memory space.
Reference types are stored in the heap—a larger memory space that is garbage collected.
Now that we’ve covered the scope of variables, let’s dive deeper into data types and how they are handled in Go.




