For statements

Go is known as a simple language, with straightforward concepts. For loops may be one of rare exceptions to that straightforward character, with three different forms: For statements A “for” statement specifies repeated execution of a block. There are three forms: The iteration may be controlled by a single condition, a “for” clause, or a “range” clause. ForStmt = "for" [ Condition | ForClause | RangeClause ] Block . Condition = Expression .


Type switches conclusion

Two final items with regard to type switches, before we move on to the next topic: Type switches … The type switch guard may be preceded by a simple statement, which executes before the guard is evaluated. This is pretty straight forward. It works just like the simple statements in if statements and normal switch statements. A simple statement can be used both with or without a temporary variable. In other words, both of these are valid:


Type switches with generics

More live coding today! Join me! And bring your questions. Type switches … A type parameter or a generic type may be used as a type in a case. If upon instantiation that type turns out to duplicate another entry in the switch, the first matching case is chosen. func f[P any](x any) int { switch x.(type) { case P: return 0 case string: return 1 case []P: return 2 case []byte: return 3 default: return 4 } } var v1 = f[string]("foo") // v1 == 0 var v2 = f[byte]([]byte{}) // v2 == 2 The example included is nice, because it shows two examples of the type parameter P being used: Both the first case (case P) and third (case []P) use the type parameter.


A long type switch example

We’ve been looking at type switches since last week. Today the spec shows us a large example, comparing type switches to the equivalent expressed without them. Type switches … Given an expression x of type interface{}, the following type switch: switch i := x.(type) { case nil: printString("x is nil") // type of i is type of x (interface{}) case int: printInt(i) // type of i is int case float64: printFloat64(i) // type of i is float64 case func(int) float64: printFunction(i) // type of i is func(int) float64 case bool, string: printString("type is bool or string") // type of i is type of x (interface{}) default: printString("don't know the type") // type of i is type of x (interface{}) } could be rewritten:

Subscribe to Boldly Go: Daily

Every day I'll send you advice to improve your understanding of Go. Don't miss out! I will respect your inbox, and honor my privacy policy.

Unsure? Browse the archive.


nils in type switches

Type switches … Instead of a type, a case may use the predeclared identifier nil; that case is selected when the expression in the TypeSwitchGuard is a nil interface value. There may be at most one nil case. Straight forward, right? Here’s an example: switch x.(type) { case int: case string: case nil: } But now here’s a question: If we assign x.(type) to a temporary variable the TypeSwitchGuard, what is its type in the nil case?


Temporary variables in type switches

Type switches … TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" . TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" . TypeCaseClause = TypeSwitchCase ":" StatementList . TypeSwitchCase = "case" TypeList | "default" . The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause.


Uniqueness of type switch cases

Finally, the last important bit from this paragraph: Type switches … Cases then match actual types T against the dynamic type of the expression x. As with type assertions, x must be of interface type, but not a type parameter, and each non-interface type T listed in a case must implement the type of x. The types listed in the cases of a type switch must all be different. Today we’re examining this part:


Type switching to non-implementing types

More live coding today! Join me in just a few hours for some more Go TDD work on my kivik library. Today we’re continuing to disect this paragraph: Type switches … Cases then match actual types T against the dynamic type of the expression x. As with type assertions, x must be of interface type, but not a type parameter, and each non-interface type T listed in a case must implement the type of x.


Type switching on a non-interface value. Sorta.

Type switches … Cases then match actual types T against the dynamic type of the expression x. As with type assertions, x must be of interface type, but not a type parameter, and each non-interface type T listed in a case must implement the type of x. The types listed in the cases of a type switch must all be different. TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .


Type switches

Back in December we looked at type assertions, which allow us to assert whether an interface type is of a specific underlying type. In that conversation, I promised we’d get to the more flexible type switches. And now, at long last, we have arrived! Type switches A type switch compares types rather than values. It is otherwise similar to an expression switch. It is marked by a special switch expression that has the form of a type assertion using the keyword type rather than an actual type:


Expression switches conclusion

We have just two more points (and some examples) before finishing the topic of expression switches, so let’s get to it. Expression switches … The switch expression may be preceded by a simple statement, which executes before the expression is evaluated. You may recall the discussion of “simple statements” from before. They can exist in an if expression as well. switch x := foo(); x { In this example, x := foo() is the simple statement.