A switch
statement is made up of case
statements and if necessary a
default
statement, if none of the case
statements match. We can think of
switch
as being a more versatile if/else
statement. We have the ability to
do everything the same as if/else
statements with a clearer and possibly more
pointed statement. We prefer a switch
over an if/else
in Go, if possible.
Setup
Let’s make our directory switches
and the files we want inside of
that directory example_test.go
switches.go
mkdir switches
touch switches/example_test.go switches/switches.go
Now let’s open up switches.go
and for the very first line we’ll add
package switches
Next for example_test.go
for the very first line we’ll add
package switches_test
We can import basics/switches
into cmd/main.go
and run functions
from there with go run cmd/main.go
and also to run our example_test.go
π
we use go test switches/example_test.go
in the commandline.
Switch Statement
In the last lesson we learned about if/else
statements. They are great, but
very limited in what they do and how they function. They are much more verbose
(they say a lot without doing much more) than simple switch
statements in Go
and it is normal in Go to be short and sweet. β€οΈ
Compare with If/Else
In programming we’re always seeking to create simple systems. The less you have
to think about, the better you will program. This goes for every software
engineer on the planet. This goes for literally every person on the planet
π, right? The less you have to focus on the more you can get your task done.
Simple. We can see with our switch
we only need to think about the very
first rune
in the provided string
and that’s it!
myStr := "Switches can focus content"
switch myStr[0] {
case 's':
// Do stuff
case 'S':
// Do stuff
default:
// Do if nothing else matches
}
myStr := "If else makes no guarantee"
if (myStr[0] == 'i') {
// Do stuff
} else if (myStr[1] == 'F') {
// Nothing to π do with first rune!
// Do stuff
} else if (myStr == "No focus") {
// Nothing to do π with runes!
// Do stuff
} else {
// Do if nothing else matches
}
With if/else
statements there is nothing driving us to only look at
the first rune
in the string
, we can look at the second one or we could
just make some other statement that has nothing to do with the topic at hand!
π¬
Coding Time!
switches.go
// Basic shows how to switch what logic to perform depending on a case
// and if that none of the criteria are met we can perform some default logic.
func Basic() {
i := 0
switch i {
case 1:
fmt.Println("i is one")
case 2:
fmt.Println("i is two")
case 3:
fmt.Println("i is three")
default:
fmt.Println("i does not have a matching case.")
}
}
example_test.go
func ExampleBasic() {
switches.Basic()
// Output:
// i does not have a matching case.
}
Multiple Cases
switch
statements don’t stop there, however. They allow for having multiple
values for the same case
. Which leads to much easier to read and understand
code.
Compare with If/Else
It does happen where you have something that can be captured under multiple
statements with if/else
and you have two choices at that point, either put
them all together in one long run on statement or break them up into multiple
else if
blocks and repeat the code β οΈ We’ll do the first one because duplicate
code is unmaintainable code.
myFluctuatingInt := 8
if myFluctuatingInt == 0 ||
myFluctuatingInt == 1 ||
myFluctuatingInt == 2 {
// Do stuff
} else if myFluctuatingInt == 3 ||
myFluctuatingInt == 4 ||
myFluctuatingInt == 5 ||
myFluctuatingInt == 6 ||
myFluctuatingInt == 7 ||
myFluctuatingInt == 8 {
// Do stuff
}
myFluctuatingInt := 8
switch myFluctuatingInt {
case 0, 1, 2:
// Do stuff
case 3, 4, 5, 6, 7, 8:
// Do stuff
}
As we can see it is much clearer to use a switch
statement, here. We
boil down what is important to look at and then allow for things to be done in
a flexible manner. This is great engineering! π
Coding Time!
switches.go
We need to add the line import time
to the top of our file, next to import fmt
for this to work. Don’t worry about it for now, we’ll understand more on
importing later.
// Multiple shows that we can perform the same logic for multiple cases
// using the same `case` keyword by comma separating the values.
func Multiple() {
month := time.August
switch month {
case time.December, time.January, time.February:
fmt.Println("Winter is here.")
case time.June, time.July, time.August:
fmt.Println("Must have some Summer flare.")
case time.October, time.November, time.September:
fmt.Println("Autumn is in the air.")
case time.March, time.April, time.May:
fmt.Println("Looks like it's Spring!")
}
}
Feel free to play around with the month and run go test
to hit the other
cases!
example_test.go
func ExampleMultiple() {
switches.Multiple()
// Output:
// Must have some Summer flare.
}
Type Assertions
In a static language (one that uses type
for everything), it’s hard to use
data coming into the application that doesn’t have a type. One example is JSON
data in web development.
When JSON data comes into the application it can be in any form! π² There could
be int
types, bool
types, string
types, []string
a string slice, or a
big giant struct{}
type, or anything! π΅ Therefore we need to be able to
assert on what that type
is and process it accordingly.
Note on The Empty Interface or The Any Type
In Go we can say the empty interface interface{}
satisfies anything and
everything! π€ Why? Because it has zero methods and therefore all types can
also implement zero methods. Though I understand the syntax can be
confusing if you’re seeing it for the first time. There is also the any
type
which is the exact same thing as interface{}
but it reads nicer π. We
will learn about
interfaces
soon enough so don’t worry
, this is confusing for now. You got this! πͺ
So if you see interface{}
anywhere you can put any
there. It’s out of scope
for what we need to learn but if you’re interested, here’s some π€
reading
material
π
Compare with If/Else
There is no comparison with if/else
in this case. It can’t do it π€·
Coding Time!
switches.go
// Type shows us that we can do type assertions using switch statements!
// This is particularly useful when getting JSON data with no idea what's
// inside.
func Type(i interface{}) {
switch t := i.(type) {
case int:
fmt.Printf("You seem like an %T-eresting type.\n", t)
case bool:
fmt.Printf("You %T! I knew it was you all along.\n", t)
case []string:
fmt.Printf("Hey, hey. Save me a slice! %T\n", t)
default:
fmt.Printf("We've never seen a %T like this.\n", t)
}
}
example_test.go
func ExampleType() {
switches.Type(true)
switches.Type(8)
switches.Type([]string{"some", "strings"})
switches.Type(struct{}{})
// Output:
// You bool! I knew it was you all along.
// You seem like an int-eresting type.
// Hey, hey. Save me a slice! []string
// We've never seen a struct {} like this.
}
Switch No Value Given
So we’ve seen switch
statements do well in a lot of areas compared to
if/else
, but one area we may see lacking is the ability to use bool
assertions! π₯² Well, fear not! We can do exactly that, in fact!
Compare with If/Else
We see in this case both switch
and if/else
perform the same. Though,
personally I find it much easier to refactor switch
statements. Meaning, I
find it much easier to move a case
statement around or add another one and in
vim
the %
key lets me go to the top of a switch
statement when I’m on the
}
unlike with an if/else
statement.
apple := "π"
if (apple == "π") {
// Do stuff
} else if (apple == "π") {
// Do stuff
} else if (apple == "π") {
// Do stuff
}
apple := "π"
switch {
case apple == "π":
// Do stuff
case apple == "π":
// Do stuff
case apple == "π":
// Do stuff
}
Coding Time!
switches.go
// NoValue shows that you don't have to give a value to the `switch`
// statement, but instead perform true or false (bool) assertions on a given
// value.
func NoValue() {
// This will get your current month! So this test may fail, see if you can't
// update it to make it pass! π
t := time.Now().Month()
switch {
case t <= time.February || t == time.December:
fmt.Println("Winter is here.")
case t <= time.May:
fmt.Println("Looks like it's Spring!")
case t <= time.August:
fmt.Println("Must have some Summer flare.")
case t <= time.November:
fmt.Println("Autumn is in the air.")
}
}
example_test.go
func ExampleNoValue() {
switches.NoValue()
// Output:
// Looks like it's Spring!
}
Fallthrough A Switch
For completeness we have the fallthrough
keyword in Go. It is pretty much
obsolete because the reason you want things to fall through would be to hit
multiple case
statements, but we already know we can do that. π And in using
it without explaining, it can lead to spaghetti π code. So it isn’t very
useful, but if you find a use let me know! π π
Compare with If/Else
if/else
has no ability to fallthrough
to another if/else
statement.
Coding Time!
switches.go
// Fallthrough shows off the **very** rarely used `fallthrough` keyword
// in Go. If you're using `fallthrough` there's probably a better solution to
// your problem.
func Fallthrough() {
switch "three" {
case "three":
fmt.Println("Floor number three")
fallthrough
case "two":
fmt.Println("Floor number two")
fallthrough
case "one":
fmt.Println("Floor number one")
fallthrough
default:
fmt.Println("Now arriving bottom floor.")
}
}
example_test.go
func ExampleFallthrough() {
switches.Fallthrough()
// Output:
// Floor number three
// Floor number two
// Floor number one
// Now arriving bottom floor.
}