Learning Golang: Scoped shorthand variable declaration
This is part 22 of my journey learning Golang.
Variable scopes
A common source of errors in programming is using the same variable in many different parts of a program. For a deep dive into the problems that this practice can cause, take a look at Global Variables Are Bad.
A common solution that is adopted by most programming languages is to make it possible to declare variables that can only be used by a subset of the program. For instance, by a single module, class, function, or statement inside of a function. This concept of the area of a program that can use a given identifier is called a “scope”. The smaller the scope, the smaller the amount of logic that can deal with that variable, and the smaller the chance of subtle errors creeping in.
Declaring a variable in the scope of a statement
Go has a shorthand notation for declaring a variable that is only “visible”, i.e. can only be used in the code that belongs to a single statement.
That can be done by assigning a value to a variable immediately after the statement keyword, and by continuing the
stament after a semi-colon (;
) character. See the examples below:
if
A variable can be declared (and initialized) before the condition of an if
statement is provided, like this:
1value := 1
2limit := 0
3if variable := value; variable > limit {
4 fmt.Println(variable)
5}
The identifier (in this case, variable
) only exists in the scope of the if
statement.
See how this can be used in a program:
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "strconv"
8 "strings"
9)
10
11func main() {
12 maxPurchase := 1000.0
13 quantity := readNumber("Quantity")
14 unitPrice := readNumber("Unit price")
15 taxes := readNumber("Taxes")
16 if total := quantity * unitPrice * (1 + taxes); total > maxPurchase {
17 fmt.Printf("Purchase denied: $%.2f is above $%.2f.\n", total, maxPurchase)
18 } else {
19 fmt.Printf("Purchase approved: $%.2f\n", total)
20 }
21}
22
23func readNumber(label string) float64 {
24 fmt.Print(label + "? ")
25 reader := bufio.NewReader(os.Stdin)
26 rawInput, _ := reader.ReadString('\n')
27 input := strings.TrimSuffix(rawInput, "\n")
28 if result, err := strconv.ParseFloat(input, 64); err == nil {
29 return result
30 }
31 panic("Invalid input.")
32}
Source: learning-go/shorthand-scoped-var/if.go
When executed, it produces an output like this:
1Quantity? 2
2Unit price? 12.99
3Taxes? 0.05
4Purchase approved: $27.28
or like this:
1Quantity? 100
2Unit price? 19.99
3Taxes? 0.12
4Purchase denied: $2238.88 is above $1000.00.
Notice that this program declares variables in the scope of an if
statement in two places: in the main
function,
and in the readNumber
function.
Notice also that the variable can be used at any place in the if
statement: in the condition, in the “then” block, and
in the else
block. But the variable is not defined outside (before or after) the if
statement. Trying to use it
outside of that scope would result in an undefined
compiler error.
Finally, for completeness, notice that other variables, like quantity
and reader
, are defined in the scope of a
function. That means that those variables can be used inside of that function, but they are not “visible” i.e. they
can’t be referred to from outside that function.
switch
The switch
statement also allows declaring a variable in its scope, like this:
1switch variable := value; variable {
2case expression:
3 doSomething()
4}
For example:
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "strings"
8)
9
10func main() {
11 fmt.Print("Which direction? ")
12 switch direction := readString(); direction {
13 case "north":
14 fmt.Printf("Far up %v you see a mountain range.\n", direction)
15 case "south":
16 fmt.Printf("Looking %v you see sand all the way to the horizon.\n", direction)
17 case "east":
18 fmt.Printf("A short distance %v you see a house by a tree.\n", direction)
19 case "west":
20 fmt.Printf("Looking %v you see the shore.\n", direction)
21 default:
22 fmt.Printf("%v is not a valid direction.\n", direction)
23 }
24}
25
26func readString() string {
27 reader := bufio.NewReader(os.Stdin)
28 rawInput, _ := reader.ReadString('\n')
29 input := strings.TrimSuffix(rawInput, "\n")
30 return input
31}
Source: learning-go/shorthand-scoped-var/switch.go
Sample output:
1Which direction? north
2Far up north you see a mountain range.
Notice how the direction
variable is declared in the scope of the switch
statement, and how it is used in the
different conditions inside of that statement.
for
One or more variables can be declared in the “init” statement of a for loop, like this:
1for variable := value; condition; post-statement {
2 doSomething()
3}
For example:
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "strconv"
8 "strings"
9)
10
11func main() {
12 iterations := readNumber("Number of iterations")
13 sum := 0
14 for i := 1; i <= iterations; i++ {
15 sum += i
16 }
17 fmt.Println(sum)
18}
19
20func readNumber(label string) int {
21 fmt.Print(label + "? ")
22 reader := bufio.NewReader(os.Stdin)
23 rawInput, _ := reader.ReadString('\n')
24 input := strings.TrimSuffix(rawInput, "\n")
25 if result, err := strconv.Atoi(input); err == nil {
26 return result
27 }
28 panic("Invalid input.")
29}
Source: learning-go/shorthand-scoped-var/for.go
This program produces an output like this:
1Number of iterations? 4
210
It’s also possible to declare more than one variable inside of a for loop:
1package main
2
3import (
4 "fmt"
5)
6
7func main() {
8 fruits := map[string]string{"a": "apple", "b": "banana"}
9 for key, value := range fruits {
10 fmt.Printf("%s is for %s.\n", key, value)
11 }
12}
Source: learning-go/shorthand-scoped-var/for-map.go
Output:
1a is for apple.
2b is for banana.
Notice how the variables key
and value
are declared in the scope of the for
loop and are used in its body.
Takeaways
- Declaring values in the smallest possible scope makes programs easier to understand and reduces the room for errors.
- Variables can be declared via shorthand notation in statements like
if
,switch
andfor
. - Variables can de used only in the scope in which they are declared. Their identifiers don’t “leak” out of that scope.
comments powered by Disqus