if & switch
Programs need to make decisions: is the student pass or fail, is it a weekend, is the input a valid number? Go has two tools for this, if and switch. Both are simpler than in C or Java: fewer brackets, no accidental fall-through, and a handy "init statement" that keeps temporary variables tidy.
In this lesson you will write if chains, use the short if form with errors, learn the guard clause style that Go programmers prefer, and use every form of switch.
if, else if and else
An if runs its block when the condition is true. Go has no brackets around the condition, but the curly braces are always required, even for one line.
package main
import "fmt"
func main() {
temp := 41 // degrees Celsius in Jaipur this afternoon
if temp >= 40 {
fmt.Println("Heatwave alert: stay indoors")
} else if temp >= 30 {
fmt.Println("Hot day: carry water")
} else {
fmt.Println("Pleasant weather")
}
}Heatwave alert: stay indoors
Go checks the conditions from top to bottom and runs only the first block whose condition is true. The else must sit on the same line as the closing } of the previous block, for the same semicolon reason you met in Your First Go Program.
The condition must be a real bool. In C or JavaScript you can write if (count), but in Go if count { } is a compile error when count is an int. Write if count > 0 { } instead. This removes a whole class of bugs.
if with a short statement
An if can start with a short statement, separated from the condition by a semicolon. Variables created there exist only inside the if and its else blocks. This is the standard way to handle errors:
package main
import (
"fmt"
"strconv"
)
func main() {
input := "17a" // typed by a customer in a quantity box
if qty, err := strconv.Atoi(input); err != nil {
fmt.Println("Invalid quantity:", err)
} else {
fmt.Println("Ordering", qty, "packets")
}
// qty and err do not exist here
}Invalid quantity: strconv.Atoi: parsing "17a": invalid syntax
Keeping qty and err inside the if means they cannot be misused later in the function, and the name err is free to reuse for the next check.
Guard clauses: return early
Long if/else ladders with deep nesting are hard to read. Go programmers prefer guard clauses: handle each special case at the top of a function and return early, so the normal path runs straight down the left edge.
package main
import "fmt"
func busFare(age int, student bool) int {
if age < 5 {
return 0
}
if age >= 60 {
return 10
}
if student {
return 15
}
return 25
}
func main() {
fmt.Println(busFare(3, false), busFare(65, false), busFare(19, true), busFare(30, false))
}0 10 15 25
No else is needed: once a return runs, the function ends. You will see this shape in almost every Go codebase, especially as if err != nil { return err }.
switch on a value
When you compare one value against many possibilities, switch is cleaner than a long if chain. Go's switch does not fall through to the next case, so there is no break to forget. A case can list several values separated by commas.
package main
import "fmt"
func main() {
day := "Sun"
switch day {
case "Sat", "Sun":
fmt.Println("Weekend: shop open 10 am to 2 pm")
case "Mon":
fmt.Println("Closed for stock-taking")
default:
fmt.Println("Open 9 am to 9 pm")
}
}Weekend: shop open 10 am to 2 pm
default runs when no case matches. It is optional and can appear anywhere, but by convention it goes last.
switch with no value
A switch with nothing after the keyword acts like switch true: each case is a full condition, and the first true one wins. This is often the clearest way to write ranges, such as an electricity tariff with slabs:
package main
import "fmt"
func main() {
units := 340
var bill float64
switch {
case units <= 100:
bill = float64(units) * 3.50
case units <= 300:
bill = 100*3.50 + float64(units-100)*5.00
default:
bill = 100*3.50 + 200*5.00 + float64(units-300)*7.25
}
fmt.Printf("Units: %d, Bill: ₹%.2f\n", units, bill)
}Units: 340, Bill: ₹1640.00
The first 100 units cost ₹350, the next 200 cost ₹1,000, and the last 40 cost ₹290. A switch can also have an init statement, like if: switch n := len(cart); { ... }.
fallthrough
If you really want the next case to run as well, write fallthrough as the last line of a case. It jumps into the next case's body without checking its condition. It is rare, but it fits rules where higher levels include everything below them:
package main
import "fmt"
func main() {
tier := 2 // frequent flyer tier
switch tier {
case 3:
fmt.Println("Lounge access")
fallthrough
case 2:
fmt.Println("Free seat selection")
fallthrough
case 1:
fmt.Println("Priority check-in")
}
}Free seat selection Priority check-in
There is also a type switch, which chooses a case by the type of a value rather than the value itself. It needs interfaces, so it is covered in Interfaces.
Common mistakes
- Adding
breakto every case. It is harmless but unnecessary; Go never falls through unless you writefallthrough. - Using a non-bool condition.
if n {}fails for numbers; compare explicitly. - Putting
elseon a new line.}andelsemust be on the same line. - Creating
errwith:=inside anifand expecting it outside. Variables from the short statement end with theif.
Key points
ifconditions have no brackets, must bebool, and always use braces.if x, err := f(); err != nil { }keeps temporary variables inside theif.- Guard clauses with early
returnkeep code flat and readable. switchcases do not fall through; list several values with commas.- A
switchwith no value tests full conditions;fallthroughforces the next case to run.
Exercise
Write a function examResult(marks int) string for a Pune college using a tagless switch: 75 and above is "Distinction", 60 to 74 is "First class", 40 to 59 is "Pass", below 40 is "Fail", and anything below 0 or above 100 is "Invalid marks" (check this first with a guard clause). Print the result for 82, 64, 39 and 105.
Show solution
package main
import "fmt"
func examResult(marks int) string {
if marks < 0 || marks > 100 {
return "Invalid marks"
}
switch {
case marks >= 75:
return "Distinction"
case marks >= 60:
return "First class"
case marks >= 40:
return "Pass"
default:
return "Fail"
}
}
func main() {
fmt.Println(examResult(82))
fmt.Println(examResult(64))
fmt.Println(examResult(39))
fmt.Println(examResult(105))
}Distinction First class Fail Invalid marks
The guard clause rejects impossible marks first, so the switch only deals with valid ones. Cases are checked in order, so each one only needs a lower limit.
Quick Quiz
1. What happens after a matching case in a Go switch finishes?
Go cases end automatically. Only an explicit fallthrough continues to the next case.
2. Where can you use qty in if qty, err := strconv.Atoi(s); err != nil { ... } else { ... }?
Variables declared in an if short statement are scoped to that if and its else branches.
3. Which is valid Go?
Conditions must be bool, and braces are always required.
4. What does a switch with no expression after the keyword compare against?
switch { ... } is the same as switch true { ... }.
Learn Go with a mentor
Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
