Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 1 | # Switch |
| 2 | |
Baiju Muthukadan | 0384a10 | 2016-03-20 08:46:31 +0530 | [diff] [blame] | 3 | Spec: https://golang.org/ref/spec#Switch_statements |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 4 | |
| 5 | Go's ` switch ` statements are pretty neat. For one thing, you don't need to break at the end of each case. |
| 6 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 7 | ```go |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 8 | switch c { |
| 9 | case '&': |
| 10 | esc = "&" |
| 11 | case '\'': |
| 12 | esc = "'" |
| 13 | case '<': |
| 14 | esc = "<" |
| 15 | case '>': |
| 16 | esc = ">" |
| 17 | case '"': |
| 18 | esc = """ |
| 19 | default: |
| 20 | panic("unrecognized escape character") |
| 21 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 22 | ``` |
| 23 | |
| 24 | [src/pkg/html/escape.go](http://golang.org/src/pkg/html/escape.go#L178) |
| 25 | |
| 26 | ## Not just integers |
| 27 | |
| 28 | Switches work on values of any type. |
| 29 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 30 | ```go |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 31 | switch syscall.OS { |
| 32 | case "windows": |
| 33 | sd = &sysDir{ |
| 34 | Getenv("SystemRoot") + `\system32\drivers\etc`, |
| 35 | []string{ |
| 36 | "hosts", |
| 37 | "networks", |
| 38 | "protocol", |
| 39 | "services", |
| 40 | }, |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 41 | } |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 42 | case "plan9": |
| 43 | sd = &sysDir{ |
| 44 | "/lib/ndb", |
| 45 | []string{ |
| 46 | "common", |
| 47 | "local", |
| 48 | }, |
| 49 | } |
| 50 | default: |
| 51 | sd = &sysDir{ |
| 52 | "/etc", |
| 53 | []string{ |
| 54 | "group", |
| 55 | "hosts", |
| 56 | "passwd", |
| 57 | }, |
| 58 | } |
| 59 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 60 | ``` |
| 61 | |
| 62 | ## Missing expression |
| 63 | |
| 64 | In fact, you don't need to switch on anything at all. A switch with no value means "switch true", making it a cleaner version of an if-else chain, as in this example from Effective Go: |
| 65 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 66 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 67 | func unhex(c byte) byte { |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 68 | switch { |
| 69 | case '0' <= c && c <= '9': |
| 70 | return c - '0' |
| 71 | case 'a' <= c && c <= 'f': |
| 72 | return c - 'a' + 10 |
| 73 | case 'A' <= c && c <= 'F': |
| 74 | return c - 'A' + 10 |
| 75 | } |
| 76 | return 0 |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 77 | } |
| 78 | ``` |
| 79 | |
| 80 | ## Break |
| 81 | |
| 82 | Go's ` switch ` statements ` break ` implicitly, but ` break ` is still useful: |
| 83 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 84 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 85 | command := ReadCommand() |
| 86 | argv := strings.Fields(command) |
| 87 | switch argv[0] { |
| 88 | case "echo": |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 89 | fmt.Print(argv[1:]...) |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 90 | case "cat": |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 91 | if len(argv) <= 1 { |
| 92 | fmt.Println("Usage: cat <filename>") |
| 93 | break |
| 94 | } |
| 95 | PrintFile(argv[1]) |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 96 | default: |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 97 | fmt.Println("Unknown command; try 'echo' or 'cat'") |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 98 | } |
| 99 | ``` |
| 100 | |
| 101 | ## Fall through |
| 102 | |
| 103 | To fall through to a subsequent case, use the ` fallthrough ` keyword: |
| 104 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 105 | ```go |
Atila Romero | 63e14fa | 2018-08-02 16:21:07 -0300 | [diff] [blame] | 106 | v := 42 |
| 107 | switch v { |
| 108 | case 100: |
| 109 | fmt.Println(100) |
| 110 | fallthrough |
| 111 | case 42: |
| 112 | fmt.Println(42) |
| 113 | fallthrough |
| 114 | case 1: |
| 115 | fmt.Println(1) |
| 116 | fallthrough |
| 117 | default: |
| 118 | fmt.Println("default") |
| 119 | } |
| 120 | // Output: |
| 121 | // 42 |
| 122 | // 1 |
| 123 | // default |
| 124 | ``` |
| 125 | |
| 126 | Another example: |
| 127 | |
| 128 | ```go |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 129 | // Unpack 4 bytes into uint32 to repack into base 85 5-byte. |
| 130 | var v uint32 |
| 131 | switch len(src) { |
| 132 | default: |
| 133 | v |= uint32(src[3]) |
| 134 | fallthrough |
| 135 | case 3: |
| 136 | v |= uint32(src[2]) << 8 |
| 137 | fallthrough |
| 138 | case 2: |
| 139 | v |= uint32(src[1]) << 16 |
| 140 | fallthrough |
| 141 | case 1: |
| 142 | v |= uint32(src[0]) << 24 |
| 143 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 144 | ``` |
| 145 | [src/pkg/encoding/ascii85/ascii85.go](http://golang.org/src/pkg/encoding/ascii85/ascii85.go#L43) |
| 146 | |
| 147 | The 'fallthrough' must be the last thing in the case; you can't write something like |
| 148 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 149 | ```go |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 150 | switch { |
| 151 | case f(): |
| 152 | if g() { |
| 153 | fallthrough // Does not work! |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 154 | } |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 155 | h() |
| 156 | default: |
| 157 | error() |
| 158 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 159 | ``` |
alanfo | eda917f | 2015-10-19 10:13:00 +0100 | [diff] [blame] | 160 | However, you can work around this by using a 'labeled' `fallthrough`: |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 161 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 162 | ```go |
alanfo | eda917f | 2015-10-19 10:13:00 +0100 | [diff] [blame] | 163 | switch { |
| 164 | case f(): |
| 165 | if g() { |
| 166 | goto nextCase // Works now! |
| 167 | } |
| 168 | h() |
| 169 | break |
| 170 | nextCase: |
| 171 | fallthrough |
| 172 | default: |
| 173 | error() |
| 174 | } |
| 175 | ``` |
alexcom | 4e9ead6 | 2018-09-25 18:12:41 +0300 | [diff] [blame] | 176 | Note: `fallthrough` does not work in type switch. |
| 177 | |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 178 | ## Multiple cases |
| 179 | |
| 180 | If you want to use multiple values in the same case, use a comma-separated list. |
| 181 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 182 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 183 | func letterOp(code int) bool { |
| 184 | switch chars[code].category { |
| 185 | case "Lu", "Ll", "Lt", "Lm", "Lo": |
| 186 | return true |
| 187 | } |
| 188 | return false |
| 189 | } |
| 190 | ``` |
| 191 | ## Type switch |
| 192 | |
| 193 | With a type switch you can switch on the type of an interface value (only): |
| 194 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 195 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 196 | func typeName(v interface{}) string { |
| 197 | switch v.(type) { |
| 198 | case int: |
| 199 | return "int" |
| 200 | case string: |
| 201 | return "string" |
| 202 | default: |
| 203 | return "unknown" |
| 204 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 205 | } |
| 206 | ``` |
| 207 | |
| 208 | You can also declare a variable and it will have the type of each ` case `: |
| 209 | |
emersion | 664cdd9 | 2016-06-14 15:42:32 +0200 | [diff] [blame] | 210 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 211 | func do(v interface{}) string { |
| 212 | switch u := v.(type) { |
| 213 | case int: |
| 214 | return strconv.Itoa(u*2) // u has type int |
| 215 | case string: |
| 216 | mid := len(u) / 2 // split - u has type string |
| 217 | return u[mid:] + u[:mid] // join |
| 218 | } |
| 219 | return "unknown" |
| 220 | } |
| 221 | |
| 222 | do(21) == "42" |
| 223 | do("bitrab") == "rabbit" |
| 224 | do(3.142) == "unknown" |
Ivan Vazquez | 77e5dbb | 2017-03-30 14:20:45 -0700 | [diff] [blame] | 225 | ``` |
| 226 | |
| 227 | ## Noop case |
| 228 | |
| 229 | Sometimes it useful to have cases that require no action. This can look confusing, because it can appear that both the noop case and the subsequent case have the same action, but isn't so. |
| 230 | |
| 231 | ```go |
| 232 | func pluralEnding(n int) string { |
| 233 | ending := "" |
| 234 | |
| 235 | switch n { |
| 236 | case 1: |
| 237 | default: |
| 238 | ending = "s" |
| 239 | } |
| 240 | |
| 241 | return ending |
| 242 | } |
| 243 | |
| 244 | fmt.Sprintf("foo%s\n", pluralEnding(1)) == "foo" |
| 245 | fmt.Sprintf("bar%s\n", pluralEnding(2)) == "bars" |
| 246 | |
| 247 | ``` |
| 248 | |