Switch statements

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:

  • Semantically, Go's switch statement can be used in two contexts:
    • An expressionswitch statement
    • A typeswitch statement
  • The break statement can be used to escape out of a switch code block early.
  • The 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.

Using expression switches

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:

  • Expression switches can test values of any types. For instance, the following code snippet (from the previous program listing) tests variable 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 
          } 
  • The expressions in 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.
  • Upon encountering the first case that matches the 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 
            } 
          } 
    
  • The 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 
          } 
        } 
    

The fallthrough cases

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 
}  

Expressionless switches

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) 
    } 
  } 
} 

Switch initializer

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.

Type switches

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)  
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.239.182