Skip to content

Latest commit

 

History

History
225 lines (171 loc) · 3.68 KB

2.4.md

File metadata and controls

225 lines (171 loc) · 3.68 KB

Control Flow

Now that we have variables, we can begin adding control logic.

Concepts

If-else

Control logic must be contained in a function:

func main() {
  x := rand.Int()
  if x < 20 {
    fmt.Println(x)
  }
}

x < 20 is a boolean expression. You can put any boolean expression in an if statement:

if ((x < 20) && (x != 15)) || (x > 100) {
  ...
}

You can also include a short statement before the boolean expression:

if x := 12 * 17; x < 20 {
  ...
}

You can also use else and else if:

if x := 12 * 17; x < 20 {
  ...
} else if x < 30 {
  ...
} else {
  ...
}

Switch

Switch statements help reduce complexity of many if branches.

i := "Monday"
switch i {
case "Monday":
  fmt.Println(":(")
case "Tuesday", "Wednesday":
  fmt.Println(":|")
default:
  fmt.Println(":)")
}

Let's dissect this:

switch i {

  • i is a primary expression that will be compared with each case.

case "Monday":

  • If i matches Monday, execute the block and exit the switch statement.

case "Tuesday", "Wednesday":

  • If i matches either of these days, execute and exit the switch statement.

default:

  • Similar to the else block.

Switch statements can be written without the initial expression also, if the case expressions are boolean:

i := 10
switch {
case i < 5:
  ...
case i > 100 && i < 200:
  ...
default:
  ...
}

The last thing switches can be used for is branching on type:

...
x := someFunc()

switch x.(type) {
case nil:
  ...
case *int, *int32:
  ...
case string:
  ...
default:
  ...
}

This may seem useless now because the type of the variable is obvious, but it will become more clear why this is important when interfaces are introduced.

For

for can operate as a 3 statement for loop (similar to c), a while loop, and an infinite loop

for i := 0; i < 5; i++ {
  fmt.Println(i)
}

i := 0
for i < 5 {
  fmt.Println(i)
  i = i + 2
}

for {
  ...
}

break / continue

break allows you to exit a loop before the for condition is met:

for i := 5; i < 200; i++ {
  if i > 12 && i % 2 == 0 {
    break
  }
}

continue allows you skip the iteration of code and continue at the top of the for loop.

for i := 0; i < 10; i++ {
  if i % 2 == 0 {
    continue
  }
  fmt.Println(i) // Only prints odds
}

Defer

defer tells the compiler to run a function before exiting the current function. This is often used to keep cleanup code near instantiating code.

package main

import (
  "fmt"
  "os"
)

func main() {
  f, err := os.Create("test.json")
  if err != nil {
    panic(err)
  }
  defer f.Close()

  fmt.Fprintln(f, "test!") // f.Close has not yet been called
} // f.Close has been called

Panic

panic is used when something has gone wrong, and you don't want to handle the error and continue. If you would like to handle errors, you can use the error type.

...

func main() {
  x := rand.Int()
  if x > 100 {
    panic("Number too large for function") // A contrived example.
  }
  someFunction(x)
}

Although panic is used when you do not want to continue running the program, you can use recover to do any cleanup tasks necessary before exiting:

...

func main() {
  defer func() {
    if a := recover(); a != nil {
      fmt.Println("RECOVER", a)
    }
  }()

  x := rand.Int()
  if x > 100 {
    panic("hello")
  }
}

recover needs to be in a deferred function, so it is impossible to return control to the calling function.

Exercises

  • What happens when you have more than one defer in a function?

Tips

  • Anonymous functions and defer work great together.

Further Reading


prev-- up