Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 1 | # Switch |
| 2 | |
| 3 | Spec: http://golang.org/doc/go_spec.html#Switch_statements |
| 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 | |
| 7 | ``` |
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 | |
| 30 | ``` |
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 | |
| 66 | ``` |
| 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 | |
| 84 | ``` |
| 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 | |
| 105 | ``` |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 106 | // Unpack 4 bytes into uint32 to repack into base 85 5-byte. |
| 107 | var v uint32 |
| 108 | switch len(src) { |
| 109 | default: |
| 110 | v |= uint32(src[3]) |
| 111 | fallthrough |
| 112 | case 3: |
| 113 | v |= uint32(src[2]) << 8 |
| 114 | fallthrough |
| 115 | case 2: |
| 116 | v |= uint32(src[1]) << 16 |
| 117 | fallthrough |
| 118 | case 1: |
| 119 | v |= uint32(src[0]) << 24 |
| 120 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 121 | ``` |
| 122 | [src/pkg/encoding/ascii85/ascii85.go](http://golang.org/src/pkg/encoding/ascii85/ascii85.go#L43) |
| 123 | |
| 124 | The 'fallthrough' must be the last thing in the case; you can't write something like |
| 125 | |
| 126 | ``` |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 127 | switch { |
| 128 | case f(): |
| 129 | if g() { |
| 130 | fallthrough // Does not work! |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 131 | } |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 132 | h() |
| 133 | default: |
| 134 | error() |
| 135 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 136 | ``` |
alanfo | eda917f | 2015-10-19 10:13:00 +0100 | [diff] [blame] | 137 | However, you can work around this by using a 'labeled' `fallthrough`: |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 138 | |
alanfo | eda917f | 2015-10-19 10:13:00 +0100 | [diff] [blame] | 139 | ``` |
| 140 | switch { |
| 141 | case f(): |
| 142 | if g() { |
| 143 | goto nextCase // Works now! |
| 144 | } |
| 145 | h() |
| 146 | break |
| 147 | nextCase: |
| 148 | fallthrough |
| 149 | default: |
| 150 | error() |
| 151 | } |
| 152 | ``` |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 153 | ## Multiple cases |
| 154 | |
| 155 | If you want to use multiple values in the same case, use a comma-separated list. |
| 156 | |
| 157 | ``` |
| 158 | func letterOp(code int) bool { |
| 159 | switch chars[code].category { |
| 160 | case "Lu", "Ll", "Lt", "Lm", "Lo": |
| 161 | return true |
| 162 | } |
| 163 | return false |
| 164 | } |
| 165 | ``` |
| 166 | ## Type switch |
| 167 | |
| 168 | With a type switch you can switch on the type of an interface value (only): |
| 169 | |
| 170 | ``` |
| 171 | func typeName(v interface{}) string { |
| 172 | switch v.(type) { |
| 173 | case int: |
| 174 | return "int" |
| 175 | case string: |
| 176 | return "string" |
| 177 | default: |
| 178 | return "unknown" |
| 179 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 180 | } |
| 181 | ``` |
| 182 | |
| 183 | You can also declare a variable and it will have the type of each ` case `: |
| 184 | |
| 185 | ``` |
| 186 | func do(v interface{}) string { |
| 187 | switch u := v.(type) { |
| 188 | case int: |
| 189 | return strconv.Itoa(u*2) // u has type int |
| 190 | case string: |
| 191 | mid := len(u) / 2 // split - u has type string |
| 192 | return u[mid:] + u[:mid] // join |
| 193 | } |
| 194 | return "unknown" |
| 195 | } |
| 196 | |
| 197 | do(21) == "42" |
| 198 | do("bitrab") == "rabbit" |
| 199 | do(3.142) == "unknown" |
| 200 | ``` |