youngman2_00

By Nathan Youngman

In this article, excerpted from Learn Go, I will introduce you to the concept of types in Go.

Every variable has a type. The type determines the values a variable can hold. Take an example from the real world. There are millions of phone numbers in the U.S., each number is a value, whereas a type would define the 10 digit representation.

Go doesn’t provide a phone number type out-of-the-box, but you can build one from primitive types. These include a string type for multilingual text, a Boolean type for true and false, and numerous numeric types for any occasion.

The word Go and the number 28487 are both represented with the same zeroes and ones on an x86 computer (0110111101000111). The type establishes what those bits and bytes mean. One is a string of two characters, the other is a 16-bit integer (2 bytes).

A variable’s type also establishes what behavior is appropriate for it. Numbers can be divided, text can be converted to uppercase, and phone numbers can be dialed. Likewise, nonsensical behavior is prevented. By using types, the Go compiler will report an error if you attempt to uppercase a number or perform division on text.


The unseen types

Every variable has a type. When you don’t specify a type, Go will determine a variable’s type for you. The following three lines of code are equivalent, because the Go compiler will infer that countdown is a string, based on the quoted text to its right.

countdown := "Launch in T minus 10 seconds." ❶ var
countdown = "Launch in T minus 10 seconds." var
countdown string = "Launch in T minus 10 seconds."

short declaration syntax

While it is valuable to know that countdown has a string type, it is superfluous to specify string. You, me, and the Go compiler can all infer that countdown is a string of text just by looking.

The golint tool, which will be discussed further in Appendix B, provides hints for coding style. It discourages the clutter with the following message:

“should omit type string from declaration of var countdown; it will be inferred from the right-hand side”

— golint

It isn’t an error to specify string, so you can if you believe it adds clarity. But as a matter of style, try to remember which types Go will infer, and rely on type inference. It will make your code less noisy.


Types can be inspected

If you are curious which type the Go compiler inferred, the Printf function provides the %T format verb to display a variable’s type.


Listing 1  Inspect a variable’s type: inspect.go

countdown := "Launch in T minus 10 seconds."
 fmt.Printf("%T", countdown) 

Print string


Experiment

Navigate to the Go Playground at play.golang.org and type Listing 1 into the body of the main function.

  • Declare countdown with different values and run the program to see which types Go infers. Use a whole number, a real number, and the word true (without quotes).
  • Write down the types Go infers for the values you used. These are the most common types.

Types have default values

In Listing 1, the countdown variable is declared and initialized with a value in one step. It is also possible to declare a variable with no value and assign a value to it later:

var countdown string
 countdown = "Launch in T minus 10
 seconds." fmt.Println(countdown)

This is handy when you want to assign a value conditionally and still have the variable available outside the scope of the condition:


Listing 2  Assigning a value later: default.go

launch := false
  
 var countdown string
  
 if launch {
     countdown = "Launch in T minus 10 seconds."
 }
 fmt.Println(countdown)

 

If the rocket is not ready to launch, countdown will not be assigned a new value, so the default value of countdown is displayed.

In Go, each type has a default value, called the zero value. The zero value for Booleans is false. For a string, the zero value is an empty string. Unsurprisingly, the zero value for numeric types is zero.


Listing 2 declares countdown with no value, which has identical behavior to:

countdown := ""

 

The difference is a matter of style.

In Listing 2, the launch variable is initialized explicitly to false, even though false is the default value for Booleans. This suggests that there is an important reason for launch to be false.

On the other hand, not specifying a value for countdown hints that the real value is yet to come.


Types are static

In Go, once a variable is declared, it has a type and that type cannot be changed. Attempting to use a variable with a value of a different type will cause the Go compiler to report an error:

var greeting = "Hello Universe" greeting
 = 42 
   var num = 42 num
 = 3.14 

This reports an error: greeting can only store text

This reports an error: num can only store whole numbers

The term for this behavior is static typing. Another class of languages use dynamic typing, where each value has an associated type, and variables can hold values of any type. In those languages (JavaScript, Python, Ruby, etc.) code similar to the above would execute.

Note

Go does support dynamic typing for situations where a type is unknown. For example, you can pass either text or numbers to the Print function.


Types don’t mix

A variable’s type establishes the behavior that is appropriate for it. Numbers can be added, strings can be joined. To join two strings together, use the plus operator:

countdown := "Launch in T minus " + "10 seconds."

If you try to join a number to a string, the Go compiler will report an error:

countdown := "Launch in T minus " + 10 + " seconds." 

invalid operation: mismatched types string and int

Sidebar

Mixing types in other languages

When presented with two or more different types, some programming languages make a best effort to guess the programmer’s intentions. Both JavaScript and PHP can subtract 1 from the string "10", whereas the Go compiler will report an error.

 "10" - 1

9 in JavaScript and PHP

To accomplish this in Go, you will need to first convert "10" to an integer. The Atoi function in the strconv package will do the conversion, but it will return an error if the string doesn’t contain a valid number. By the time you handle errors, the Go version is four lines long, which is certainly less convenient.

That said, if "10" is user input or came from an external source, the JavaScript and PHP versions should check if it’s a valid number too.

In languages that coerce types, the code’s behavior is less predictable to anyone who hasn’t memorized a myriad of implicit behaviors. The plus operator in both Java and JavaScript coerces numbers to strings to be joined, whereas PHP coerces the values to numbers and does the math. Once again, Go reports a mismatched types error.

 "10" + 2

“102” in JavaScript or Java, 12 in PHP

Another example of mismatched types occurs when attempting a calculation with a mix of integer and floating point types. Real numbers like 365.2425 are represented with a floating point type, while Go infers that whole numbers are integers.

age := 38                                                         
marsDays := 687                                                   
earthDays := 365.2425                                             
fmt.Println("I am", age*earthDays/marsDays, "years old on Mars.") 

age and marsDays are integers

earthDays is a floating point type

invalid operation: mismatched types

If all three variables were integers, the calculation would succeed, but then earthDays would need to be 365 instead of the more accurate 365.2425.

Alternatively, the calculation would succeed if age and marsDays were floating point types (38.0 and 687.0 respectively). Go doesn’t make assumptions about which you would prefer.