# Conditions and Switch

> Source: https://learn-go.org/conditions/
> Part of Learn Go, free to read.

```go
if score > 90 {
    fmt.Println("excellent")
} else if score > 70 {
    fmt.Println("good")
} else {
    fmt.Println("keep going")
}
```

No parentheses around the condition, and the braces are mandatory even for one line. The opening brace must be on the same line — Go's automatic semicolon insertion breaks the statement otherwise, which is why `gofmt` will not let you move it.

## The statement-scoped if

The idiom you will write most often in Go:

```go
if err := doSomething(); err != nil {
    return fmt.Errorf("do something: %w", err)
}
// err does not exist here
```

The initialiser runs first, then the condition. Anything it declares is scoped to the `if` and its `else` branches — which keeps short-lived variables like `err` out of the enclosing function.

```go
if user, err := findUser(id); err != nil {
    return err
} else {
    fmt.Println(user.Name)      // both user and err visible here
}
```

In practice you rarely need the `else` — returning early is the dominant Go style:

```go
user, err := findUser(id)
if err != nil {
    return err
}
fmt.Println(user.Name)          // the happy path stays unindented
```

## switch

Go's switch has no automatic fallthrough, so no `break` is needed:

```go
switch status {
case "pending":
    fmt.Println("waiting")
case "shipped", "delivered":     // several values in one case
    fmt.Println("on its way")
default:
    fmt.Println("unknown")
}
```

### switch with no condition

A `switch` with nothing after it means `switch true`, which replaces a long if/else chain:

```go
switch {
case score > 90:
    grade = "A"
case score > 70:
    grade = "B"
default:
    grade = "C"
}
```

This is idiomatic Go and usually reads better than the equivalent `else if` ladder.

### switch with an initialiser

```go
switch hour := time.Now().Hour(); {
case hour < 12:
    fmt.Println("morning")
case hour < 18:
    fmt.Println("afternoon")
default:
    fmt.Println("evening")
}
```

### fallthrough

Available, and rare:

```go
switch n {
case 1:
    fmt.Println("one")
    fallthrough        // continue into the next case regardless of its condition
case 2:
    fmt.Println("two")
}
// n == 1 prints both
```

## Type switches

A switch on the dynamic type of an interface value. This is how you handle a value that could be several things:

```go
func describe(v any) string {
    switch x := v.(type) {
    case nil:
        return "nothing"
    case int:
        return fmt.Sprintf("int %d", x*2)      // x is an int here
    case string:
        return "string of length " + strconv.Itoa(len(x))
    case []int:
        return fmt.Sprintf("%d ints", len(x))
    case error:
        return "error: " + x.Error()
    default:
        return fmt.Sprintf("unhandled %T", x)
    }
}
```

Inside each case, `x` has that specific type. `%T` in the default branch prints the actual type, which is invaluable when debugging.

## Comparison and logic

```go
==  !=  <  <=  >  >=
&&  ||  !
```

`&&` and `||` short-circuit. Two Go-specific points:

**There is no ternary operator.** Write the `if`, or a small helper:

```go
label := "off"
if enabled {
    label = "on"
}
```

**There are no truthy values.** A condition must be a `bool`:

```go
if count { }        // error: non-boolean condition
if count != 0 { }   // fine
if name != "" { }   // fine
```

That strictness removes a whole class of bug found in dynamically typed languages — and it is one reason generated Go is less likely to be subtly wrong than generated JavaScript.

## Exercise

```go
package main

import "fmt"

func main() {
	scores := []int{95, 72, 58, 88, 100}
	// For each score print "<score>: <grade>" where grade is
	//   A for 90+, B for 80-89, C for 70-79, F below 70.
	// Use a conditionless switch.
	fmt.Println(scores)
}
```

## Common questions

### Why is there no ternary operator?

A deliberate omission — the Go authors judged that nested ternaries hurt readability more than the brevity helps. The three-line `if` is the intended form, and in practice it is rarely the thing that makes Go verbose.

### When should I use a type switch?

When handling a value whose type genuinely varies — parsing arbitrary JSON, walking an AST, or inspecting an error chain. If you find yourself type-switching on your own types regularly, an interface with a method is usually the better design.

### Should I use `else`?

Sparingly. The dominant Go style is to handle the error case and return early, leaving the happy path unindented. A function whose main logic sits inside an `else` block is usually one early return away from being clearer.
