Go also supports a switch
statement similar to that found in other languages such as, C or Java. The switch
statement in Go achieves multi-way branching by evaluating values or expressions from case
clauses, as shown in the following, abbreviated, source code:
import "fmt" type Curr struct { Currency string Name string Country string Number int } var currencies = []Curr{ Curr{"DZD", "Algerian Dinar", "Algeria", 12}, Curr{"AUD", "Australian Dollar", "Australia", 36}, Curr{"EUR", "Euro", "Belgium", 978}, Curr{"CLP", "Chilean Peso", "Chile", 152}, Curr{"EUR", "Euro", "Greece", 978}, Curr{"HTG", "Gourde", "Haiti", 332}, ... } func isDollar(curr Curr) bool { var bool result switch curr { default: result = false case Curr{"AUD", "Australian Dollar", "Australia", 36}: result = true case Curr{"HKD", "Hong Kong Dollar", "Hong Koong", 344}: result = true case Curr{"USD", "US Dollar", "United States", 840}: result = true } return result } func isDollar2(curr Curr) bool { dollars := []Curr{currencies[2], currencies[6], currencies[9]} switch curr { default: return false case dollars[0]: fallthrough case dollars[1]: fallthrough case dollars[2]: return true } return false } func isEuro(curr Curr) bool { switch curr { case currencies[2], currencies[4], currencies[10]: return true default: return false } } func main() { curr := Curr{"EUR", "Euro", "Italy", 978} if isDollar(curr) { fmt.Printf("%+v is Dollar currency ", curr) } else if isEuro(curr) { fmt.Printf("%+v is Euro currency ", curr) } else { fmt.Println("Currency is not Dollar or Euro") } dol := Curr{"HKD", "Hong Kong Dollar", "Hong Koong", 344} if isDollar2(dol) { fmt.Println("Dollar currency found:", dol) } }
golang.fyi/ch03/switchstmt.go
The switch
statement in Go has some interesting properties and rules that make it easy to use and reason about:
switch
statement can be used in two contexts:switch
statementswitch
statementbreak
statement can be used to escape out of a switch code block early.switch
statement can include a default case when no other case expressions evaluate to a match. There can only be one default case and it may be placed anywhere within the switch block.Expression switches are flexible and can be used in many contexts where control flow of a program needs to follow multiple path. An expression switch supports many attributes, as outlined in the following bullets:
Curr
of type struct
:func isDollar(curr Curr) bool { var bool result switch curr { default: result = false case Curr{"AUD", "Australian Dollar", "Australia", 36}: result = true case Curr{"HKD", "Hong Kong Dollar", "Hong Koong", 344}: result = true case Curr{"USD", "US Dollar", "United States", 840}: result = true } return result }
case
clauses are evaluated from left to right, top to bottom, until a value (or expression) is found that is equal to that of the switch
expression.switch
expression, the program will execute the statements for the case
block and then immediately exit the switch
block. Unlike other languages, the Go case
statement does not need to use a break to avoid falling through the next case (see the Fallthrough cases section). For instance, calling isDollar(Curr{"HKD", "Hong Kong Dollar", "Hong Kong", 344})
will match the second case
statement in the preceding function. The code will set the result to true
and exit the switch
code block immediately.Case
clauses can have multiple values (or expressions) separated by commas with a logical OR
operator implied between them. For instance, in the following snippet, the switch
expression curr
is tested against values currencies[2]
, currencies[4]
, or currencies[10]
, using one case clause until a match is found:func isEuro(curr Curr) bool { switch curr { case currencies[2], currencies[4], currencies[10]: return true default: return false } }
switch
statement is the cleaner and preferred idiomatic approach to writing complex conditional statements in Go. This is evident when the preceding snippet is compared to the following, which does the same comparison using if
statements:func isEuro(curr Curr) bool { if curr == currencies[2] || curr == currencies[4], curr == currencies[10]{ return true }else{ return false } }
There is no automatic fall through in Go's case
clause as there is in the C or Java switch
statements. Recall that a switch
block will exit after executing its first matching case. The code must explicitly place the fallthrough
keyword, as the last statement in a case
block, to force the execution flow to fall through the successive case
block. The following code snippet shows a switch
statement with a fallthrough
in each case block:
func isDollar2(curr Curr) bool { switch curr { case Curr{"AUD", "Australian Dollar", "Australia", 36}: fallthrough case Curr{"HKD", "Hong Kong Dollar", "Hong Kong", 344}: fallthrough case Curr{"USD", "US Dollar", "United States", 840}: return true default: return false } }
golang.fyi/ch03/switchstmt.go
When a case is matched, the fallthrough
statements cascade down to the first statement of the successive case
block. So, if curr = Curr{"AUD", "Australian Dollar", "Australia", 36}
, the first case will be matched. Then the flow cascades down to the first statement of the second case block, which is also a fallthrough
statement. This causes the first statement, to return true
, of the third case block to execute. This is functionally equivalent to the following snippet:
switch curr { case Curr{"AUD", "Australian Dollar", "Australia", 36}, Curr{"HKD", "Hong Kong Dollar", "Hong Kong", 344}, Curr{"USD", "US Dollar", "United States", 840}: return true default: return false }
Go supports a form of the switch
statement that does not specify an expression. In this format, each case
expression must evaluate to a Boolean value true
. The following abbreviated source code illustrates the uses of an expressionless switch
statement, as listed in function find()
. The function loops through the slice of Curr
values to search for a match based on field values in the struct
function that's passed in:
import ( "fmt" "strings" ) type Curr struct { Currency string Name string Country string Number int } var currencies = []Curr{ Curr{"DZD", "Algerian Dinar", "Algeria", 12}, Curr{"AUD", "Australian Dollar", "Australia", 36}, Curr{"EUR", "Euro", "Belgium", 978}, Curr{"CLP", "Chilean Peso", "Chile", 152}, ... } func find(name string) { for i := 0; i < 10; i++ { c := currencies[i] switch { case strings.Contains(c.Currency, name), strings.Contains(c.Name, name), strings.Contains(c.Country, name): fmt.Println("Found", c) } } }
golang.fyi/ch03/switchstmt2.go
Notice in the previous example, the switch
statement in function find()
does not include an expression. Each case
expression is separated by a comma and must be evaluated to a Boolean value with an implied OR
operator between each. The previous switch
statement is equivalent to the following use of an if
statement to achieve the same logic:
func find(name string) { for I := 0; i < 10; i++ { c := currencies[i] if strings.Contains(c.Currency, name) || strings.Contains(c.Name, name) || strings.Contains(c.Country, name){ fmt.Println""Foun"", c) } } }
The switch
keyword may be immediately followed by a simple initialization statement where variables, local to the switch
code block, may be declared and initialized. This convenient syntax uses a semi-colon between the initializer statement and the switch
expression to declare variables, which may appear anywhere in the switch
code block. The following code sample shows how this is done by initializing two variables, name
and curr
, as part of the switch
declaration:
func assertEuro(c Curr) bool { switch name, curr := "Euro", "EUR"; { case c.Name == name: return true case c.Currency == curr: return true } return false }
golang.fyi/ch03/switchstmt2.go
The previous code snippet uses an expressionless switch
statement with an initializer. Notice the trailing semi-colon to indicate the separation between the initialization statement and the expression area for the switch. In the example, however, the switch
expression is empty.
Given Go's strong type support, it should be of little surprise that the language supports the ability to query type information. The type switch
is a statement that uses the Go interface type to compare the underlying type information of values (or expressions). A full discussion on interface types and type assertion is beyond the scope of this section. You can find more details on the subject in Chapter 8, Methods, Interfaces, and Objects.
Nevertheless, for the sake of completeness, a short discussion on type switches is provided here. For now, all you need to know is that Go offers the type interface{}
, or empty interface, as a super type that is implemented by all other types in the type system. When a value is assigned type interface{}
, it can be queried using the type switch
, as shown in function findAny()
in the following code snippet, to query information about its underlying type:
func find(name string) { for i := 0; i < 10; i++ { c := currencies[i] switch { case strings.Contains(c.Currency, name), strings.Contains(c.Name, name), strings.Contains(c.Country, name): fmt.Println("Found", c) } } } func findNumber(num int) { for _, curr := range currencies { if curr.Number == num { fmt.Println("Found", curr) } } } func findAny(val interface{}) { switch i := val.(type) { case int: findNumber(i) case string: find(i) default: fmt.Printf("Unable to search with type %T ", val) } } func main() { findAny("Peso") findAny(404) findAny(978) findAny(false) }
golang.fyi/ch03/switchstmt2.go
The function findAny()
takes an interface{}
as its parameter. The type switch
is used to determine the underlying type and value of the variable val
using the type assertion expression:
switch i := val.(type)
Notice the use of the keyword type
in the preceding type assertion expression. Each case clause will be tested against the type information queried from val.(type)
. Variable i
will be assigned the actual value of the underlying type and is used to invoke a function with the respective value. The default block is invoked to guard against any unexpected type assigned to the parameter val
parameter. Function findAny
may then be invoked with values of diverse types, as shown in the following code snippet:
findAny("Peso") findAny(404) findAny(978) findAny(false)
18.191.74.66