# 03. Flow Control

## `if` условие

Условное выражение `if` в Go:

```go
func sqrt(x float64) string {
    if x < 0 {
        return sqrt(-x) + "i"
    }
    return fmt.Sprint(math.Sqrt(x))
}
```

Like `for`, the `if` statement can start with a short statement to execute before the condition.

* Variables declared by the statement are only in scope until the end of the `if`.
* Variables declared inside an `if` short statement are also available inside any of the else blocks.

```go
func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    } else {
        fmt.Printf("%g >= %g\n", v, lim)
    }
    return lim
}
```

## `for` loop

Go has only one looping construct, the `for` loop. The basic `for` loop has three components separated by semicolons:

* the init statement: executed before the first iteration
* the condition expression: evaluated before every iteration
* the post statement: executed at the end of every iteration

```go
for i := 0; i < 20; i++ {
    fmt.Println(i)
}
```

Классический цикл по массиву или слайсу:

```go
for i := 0; i < len(sl); i++ {
    println("c-style loop", i, sl[i])
}
```

Note: Unlike other languages like C, Java, or JavaScript there are no parentheses surrounding the three components of the `for` statement and the braces `{ }` are always required.

The init and post statement are optional. At that point you can drop the semicolons:

```go
sum := 1
for sum < 1000 {
    sum += sum
}
```

If you omit the loop condition it loops forever, so an infinite loop is compactly expressed.

```go
for {
    println("бесконечный цикл")
    break
}
```

## Range `for` loop

* The `range` form of the `for` loop iterates over a slice, map or channel.
* When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.
* The `range` loop evaluates the provided expression only once, before the beginning of the loop, by doing a copy (regardless of the type).
* When a range loop iterates over a data structure, it performs a **copy** of each element to the value variable (the second item). To avoid copy  either use a classic for loop or a range loop using the index instead of the value variable.
* Iterating over using the range loop, regardless of the number of elements, creates a single variable for the value with a fixed address. This value is being reassigned on each iteration, but it keeps the same address.&#x20;

```go
for i, v := range pow {
    fmt.Printf("2**%d = %d\n", i, v)
}
```

You can skip the index or value by assigning to `_`.

```go
pow := make([]int, 10)
for i := range pow {
    pow[i] = 1 << uint(i) // == 2**i
}

for _, value := range pow {
    fmt.Printf("%d\n", value)
}
```

It’s important to know that `range` is making a copy of the value, not returning a reference. If you use the address of the value variable as a pointer to each element, you’ll be making a mistake.

Iterating over map you shouldn’t make any ordering assumptions at all:

* It doesn’t keep the data sorted by key (a map isn’t based on a binary tree).
* It doesn’t preserve the order in which the data was added.
* Randomize order for each iteration.
* An element being produced during the same iteration in which it’s added.

## `switch`

A `switch` statement is a shorter way to write a sequence of `if - else` statements. It runs the first case whose value is equal to the condition expression.

* Go only runs the selected case, not all the cases that follow.
* Go's switch cases need not be constants, and the values involved need not be integers.
* Switch cases evaluate cases from top to bottom, stopping when a case succeeds.
* &#x20;the order of the `default` branch in a `switch-case` control flow block can be arbitrary
* Switch without a condition is the same as switch `true`.
* `break` inside case interrupt current case
* `fallthrough` keyword used to indicate that next case should be proceed.

  ```go
  fmt.Print("Go runs on ")
  switch os := runtime.GOOS; os {
    case "darwin":
        fmt.Println("OS X.")
    case "linux":
        fmt.Println("Linux.")
    case  "windows":
        if mm["flag"] == "Ok" {
            break // выходим из switch, чтобы не выполнять переход в другой вариант
        }
        fmt.Println("Windows.")
        fallthrough // переходим в следующий вариант
    default:
        // freebsd, openbsd,
        // plan9, windows...
        fmt.Printf("%s.", os)
  }
  ```

Case could contain multiple options:

```go
func isShellSpecialVar(c uint8) bool {
    switch c {
    case '*', '#', '$', '@', '!', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
        return true
    }
    return false
}
```

**Blank switch**: If the switch has no expression it switches on true. It's therefore possible and idiomatic—to write an `if-else-if-else` chain as a switch.

```go
func unhex(c byte) byte {
    switch {
    case '0' <= c && c <= '9':
        return c - '0'
    case 'a' <= c && c <= 'f':
        return c - 'a' + 10
    case 'A' <= c && c <= 'F':
        return c - 'A' + 10
    }
    return 0
}
```

```go
switch wordLen := len(word); { 
case wordLen < 5:
        fmt.Println(word, "is a short word!") 
case wordLen > 10:
        fmt.Println(word, "is a long word!") 
default:
        fmt.Println(word, "is exactly the right length.")
}
```

Favor blank switch statements over if/else chains when you have multiple related cases. Using a switch makes the comparisons more visible and reinforces that they are a related set of concerns.

## Labels and `goto`

* A `goto` keyword must be followed by a label to form a statement.&#x20;
* A label which name is not the blank identifier must be used at least once.
* A `goto` statement will make the execution jump to the next statement following the declaration of the label used in the `goto` statement.
* if a label is declared within the scope of a variable, then the uses of the label can't appear before the declaration of the variable.

```go
Loop:
    for key, val := range mm {
        println("switch in loop", key, val)
        switch {
        case key == "firstName" && val == "Vasily":
            println("switch - break loop here")
            break Loop
        }
    }
```

```go
func main() {
	i := 0
Next:
	if i >= 5 {
		goto Exit
	}
	// Create an explicit code block to
	// shrink the scope of k.
	{
		k := i + i
		fmt.Println(k)
	}
	i++
	goto Next
Exit:
}
```

Метка может находится и ниже, но переход по ней не должен означать вводить новых переменых (что является side effect-ом).
