# 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-ом).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://strctr.gitbook.io/programming/01-languages/go-lang/02-language/03-flow-control.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
