CHAPTER 4
Go is a statically typed programming language, meaning that when you define a variable, Go expects you to tell it what type of data it will hold. You can’t then suddenly decide to store a different type of data in the same variable.
Wars have been fought, marriages have failed, and friendships been sundered as a result of programmers arguing about which is best: statically typed languages like Go, or dynamically typed languages like JavaScript. (Okay, I’m exaggerating. But nonetheless, programmers are definitely divided into camps over this.)
Go attempts to bridge the gap by being a statically typed language that nonetheless can be used in ways that makes it “feel” more like a dynamic language.
The best way to demonstrate this is with an example. Let’s take the “Hello, world!” program we built in the previous chapter and amend it so the message it displays is stored in a variable rather than hard-coded.
Code Listing 2
package main import ( "fmt" ) func main() { var msg string = "Hello, world!\n" fmt.Printf(msg) } |
Note the way we declare variables in Go. We use the var keyword, followed by the name of the variable (msg), followed by the type (in this instance, string), and then finally we assign the value (“Hello World”). This might look a bit back-to-front compared to most statically typed languages, but it’s the way Go likes to do things.
When we run it, we get exactly the same output we had before.
If we are initializing the variable at the point of declaration, we can omit the type and Go will infer it from the value we have assigned to it. So we can use the form:
var myVar = "This will use the string data type"
Go also provides us with an even more concise way of declaring a variable and assigning a value to it. Consider the following code:
Code Listing 3
package main import ( "fmt" ) func main() { msg := "Hello, world!\n" fmt.Printf(msg) } |
In this example, we’ve done away with the type declaration and the var keyword. Using this syntax feels very much like working with a dynamically typed language. However, we are not. In the presence of the := operator, Go knows that we’re assigning a value to a variable, and it infers the data type from the assignment value, just as before. If we then try to reassign a different data type to msg, we’ll get an error, proving that no matter how “dynamic” Go might look at times, it is still a statically typed language:
Code Listing 4
package main import ( "fmt" ) func main() { msg := "Hello, world!\n" msg := 9 fmt.Printf(msg) } |
./main.go:9: no new variables on left side of :=
./main.go:9: cannot use 9 (type int) as type string in assignment
In order to use the numerical value of 9, we must assign it to a different variable of numeric type. Let’s change our program to use two variables and output them both in the same print statement.
Code Listing 5
package main import ( "fmt" ) func main() { msg := "Today's prize-winning entry is %d\n" winner := 9 fmt.Printf(msg, winner) } |
Today's prize-winning entry is 9
Here we’re using string interpolation (also known as variable substitution or variable expansion) to replace a placeholder (%d) with the value of the variable winner.
Not all data is variable—sometimes we want to ensure that a value remains unchanged throughout a program. We can use the const keyword for that, and a const can appear anywhere a var can.
When you are declaring constants, you don’t have to specify the data type. You can, but you don’t have to. That’s because constants are treated differently in Go. When you don’t specify a data type for a constant, Go gives it a special “undefined” data type and converts it to an appropriate data type at the point it is used. This is powerful, because it means that constants can be accessed widely throughout your program without having to worry about mismatched data types.
If we declare constants or variables within a function body, they’ll only be available within that function. We can make them available to all functions in the main package by declaring them at the top level.
Tip: If we declare any constant or variable at the top level and capitalize the first letter of its name, it becomes truly global and accessible from outside of the main package. This is one of Go's most bizarre (yet useful) features, and it helps us avoid the need for keywords like private, public, protected, and so on.
But remember that if we declare constants or variables at the top level, we must use the var or const keywords—we cannot use the shortcut variable assignment operator :=. We can group several assignments together at the top level by surrounding them with parentheses, as shown in Code Listing 6.
Code Listing 6
package main import ( "fmt" ) const ( prizeDay = "Wednesday" prizeFund = 10000 ) func main() { msg := "%s's prize-winning entry is %d and wins %d!!!\n" winner := 9 fmt.Printf(msg, prizeDay, winner, prizeFund) } |
Wednesday's prize-winning entry is 9 and wins 10000!!!
Note: We use %s to interpolate a string and %d to interpolate an integer. See a full list of interpolation verbs here.
We can also scope variables in the same way:
Code Listing 7
package main import ( "fmt" ) var ( prizeDay = "Wednesday" prizeFund = 10000 ) func main() { prizeDay = "Thursday" prizeFund = 50000 msg := "%s's prize-winning entry is %d and wins %d!!!\n" winner := 9 fmt.Printf(msg, prizeDay, winner, prizeFund) } |
Thursday's prize-winning entry is 9 and wins 50000!!!
Because prizeDay and prizeFund are variables, we can change their values. If we had attempted to do that when they were defined as constants, we would have received the following error:
./main.go:13: cannot assign to prizeDay
./main.go:14: cannot assign to prizeFund
./main.go:14: cannot use 50000 (type int) as type untyped number in assignment
Now that we’ve seen how types are declared, let’s have a look at the range of data types available.