If
Go deliberately has fewer control-flow keywords than most languages. There's no while, no do-while, no ternary operator. Once you know why, it stops feeling like a missing feature and starts feeling like one less thing to decide between.
if
age := 20
if age >= 18 {
fmt.Println("adult")
} else if age >= 13 {
fmt.Println("teen")
} else {
fmt.Println("child")
}Two things jump out coming from most other languages: no parentheses around the condition, and the braces are mandatory — you can't write a one-line if without them. Both are enforced by the compiler, not just a style guide, which means every Go codebase you read looks like this one.
if also accepts an initializer statement — a short statement that runs right before the condition is checked, scoped to just the if/else chain. It looks unfamiliar at first, so let's build up to it in pieces, using only what you already know: := and comparisons.
Here's the same code written two ways. First, as two separate lines:
x := 10
if x > 5 {
fmt.Println("big:", x)
} else {
fmt.Println("small:", x)
}Now with the initializer folded into the if, using a semicolon to separate the setup statement from the condition:
if x := 10; x > 5 {
fmt.Println("big:", x)
} else {
fmt.Println("small:", x)
}Read it as two statements glued together — x := 10 runs first, then x > 5 is checked as the condition, exactly like the two-line version above. The semicolon is the seam: everything before it is the initializer, everything after it is the condition.
Why fold it in at all? Scope. A variable declared in the initializer only exists inside the if/else if/else chain it belongs to — not in the rest of the function:
if x := 10; x > 5 {
fmt.Println("big:", x)
}
fmt.Println(x) // compile error: undefined: xWith the two-line version, x would still be sitting in the function's scope after the if block ends — available to be misused, or to collide with another variable named x later on.
This becomes genuinely useful once a statement can produce more than one value — which is exactly what you'll see in the upcoming Functions and Errors lessons later in this module, in patterns like:
if err := doSomething(); err != nil {
return err
}Here err only needs to exist long enough to be checked, then it's gone — the next line down can declare its own err := ... with no conflict. You'll see this constantly once functions and errors are in play; for now, the thing to hold onto is just this: if can run a short setup statement right before its condition, and whatever that statement declares only lives inside that if/else chain.
No ternary operator
Python has x if cond else y; Go has nothing shorter than a full if/else. This is intentional — the language designers left it out on purpose to avoid the nested-ternary readability problem, and the idiomatic replacement is just to write the if:
status := "child"
if age >= 18 {
status = "adult"
}It's a few more lines than a ternary. Once you've read enough Go, you'll find you stop missing it.