switch

Compare one value against many cases without a chain of ifs, with no accidental fall-through between them.

Syntaxswitch init; expr { case a, b: ... default: ... }

A long chain of else if tests is hard to read. switch says once what is being compared, then lists the values to compare it against. The first matching case runs, and default runs when none match.

switch day {
case "sat", "sun":
	fmt.Println("weekend")
case "mon":
	fmt.Println("start of the week")
default:
	fmt.Println("weekday")
}

One case can list several values separated by commas, which saves repeating the body. Only the matching case runs: Go does not continue into the case below it, so no break is needed at the end of a case. If you do want to continue into the next case body, write fallthrough as the last statement of the case. It is rare.

switch with no value

Leave out the value and each case becomes a test of its own. This replaces an if-chain. The two forms below are the same thing, and the shorthand is the one you will see:

// longhand
switch true {
case n < 0:
	return "negative"
case n == 0:
	return "zero"
}

// shorthand: the true is implied
switch {
case n < 0:
	return "negative"
case n == 0:
	return "zero"
}

Like if, a switch may declare a variable first: switch n := count(); {. Cases do not have to be fixed values; any expression works.

A form to recognise later

There is a second kind, written switch x := v.(type), which branches on what type a value holds rather than what it equals. It needs interfaces, so go-type-assertions covers it.

Example

Example · go
package main

import "fmt"

// A switch with no value: each case is its own test, checked in order.
func describe(n int) string {
	switch {
	case n &lt; 0:
		return "negative"
	case n == 0:
		return "zero"
	case n &lt; 10:
		return "small"
	default:
		return "large"
	}
}

func main() {
	// A switch on a value, with two values sharing one case.
	day := "sun"
	switch day {
	case "sat", "sun":
		fmt.Println("weekend")
	default:
		fmt.Println("weekday")
	}

	for _, n := range []int{-3, 0, 7, 99} {
		fmt.Println(n, "is", describe(n))
	}
}

// Output:
// weekend
// -3 is negative
// 0 is zero
// 7 is small
// 99 is large

When to use it

  • Pick an action from a command name or a menu choice.
  • Turn a numeric score or size into a band such as small, medium or large.
  • Map a status code to a human-readable message.
  • Reject anything unrecognised in one obvious default branch.

More examples

Dispatching on a command word

Every command line tool has a switch like this near the top. The comma-separated case gives a command an alias at no extra cost, and default is where an unknown command is rejected rather than silently ignored.

Example · go
package main

import (
	"fmt"
	"os"
)

func main() {
	// os.Args holds the program name first, then the words typed after it,
	// so the arguments start at os.Args[1].
	if len(os.Args) &lt; 2 {
		fmt.Fprintln(os.Stderr, "usage: tool [add|list|version]")
		os.Exit(2)
	}

	switch os.Args[1] {
	case "add":
		fmt.Println("adding")
	case "list", "ls": // one body, two spellings
		fmt.Println("listing")
	case "version":
		fmt.Println("1.0.0")
	default:
		fmt.Fprintf(os.Stderr, "unknown command: %s\n", os.Args[1])
		os.Exit(2)
	}
}

An initialiser plus a value, and fallthrough

The initialiser computes a value used only by this switch, keeping it out of the surrounding function. fallthrough is shown because you will meet it in other people's code; note that it does not check the next case's values, it just runs its body, which is exactly why it is used sparingly.

Example · go
package main

import "fmt"

func level(score int) string {
	// The switch declares grade, then compares it.
	switch grade := score / 10; grade {
	case 10, 9:
		return "excellent"
	case 8, 7:
		return "good"
	case 6:
		// fallthrough enters the next case body without testing it.
		fallthrough
	case 5:
		return "pass"
	default:
		return "fail"
	}
}

func main() {
	for _, s := range []int{95, 72, 64, 51, 12} {
		fmt.Println(s, level(s))
	}
}

// Output:
// 95 excellent
// 72 good
// 64 pass
// 51 pass
// 12 fail

A preview of the type switch

Shown so the shape is familiar when you meet it. Instead of comparing values, the cases name types, and inside each case x already has that type, so x*2 and len(x) are allowed. The lesson on type assertions explains why this works and when it is the right tool.

Example · go
package main

import "fmt"

// any means "a value of any type at all".
func show(v any) string {
	switch x := v.(type) {
	case int:
		return fmt.Sprintf("int, doubled: %d", x*2)
	case string:
		return fmt.Sprintf("string of length %d", len(x))
	case bool:
		return fmt.Sprintf("bool: %t", x)
	default:
		return fmt.Sprintf("some other type: %T", x)
	}
}

func main() {
	fmt.Println(show(21))
	fmt.Println(show("hello"))
	fmt.Println(show(true))
	fmt.Println(show(1.5))
}

// Output:
// int, doubled: 42
// string of length 5
// bool: true
// some other type: float64

Discussion

  • Be the first to comment on this lesson.