Learning Golang: Scoped shorthand variable declaration

Share on:

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 and for.
  • 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