Types

In the last chapter we used the data type string to store Hello World. Data types categorize a set of related values, describe the operations that can be done on them and define the way they are stored. Since types can be a difficult concept to grasp we will look at them from a couple different perspectives before we see how they are implemented in Go.

Philosophers sometimes make a distinction between types and tokens. For example suppose you have a dog named Max. Max is the token (a particular instance or member) and dog is the type (the general concept). “Dog” or “dogness” describes a set of properties that all dogs have in common. Although oversimplistic we might reason like this: All dogs have 4 legs, Max is a dog, therefore Max has 4 legs. Types in programming languages work in a similar way: All strings have a length, x is a string, therefore x has a length.

In mathematics we often talk about sets. For example: ℝ (the set of all real numbers) or ℕ (the set of all natural numbers). Each member of these sets shares properties with all the other members of the set. For example all natural numbers are associative: “for all natural numbers a, b, and c, a + (b + c) = (a + b) + c and a × (b × c) = (a × b) × c.” In this way sets are similar to types in programming languages since all the values of a particular type share certain properties.

Go is a statically typed programming language. This means that variables always have a specific type and that type cannot change. Static typing may seem cumbersome at first. You'll spend a large amount of your time just trying to fix your program so that it finally compiles. But types help us reason about what our program is doing and catch a wide variety of common mistakes.

Go comes with several built-in data types which we will now look at in more detail.

Numbers

Go has several different types to represent numbers. Generally we split numbers into two different kinds: integers and floating-point numbers.

Integers

Integers – like their mathematical counterpart – are numbers without a decimal component. (…, -3, -2, -1, 0, 1, …) Unlike the base-10 decimal system we use to represent numbers, computers use a base-2 binary system.

Our system is made up of 10 different digits. Once we've exhausted our available digits we represent larger numbers by using 2 (then 3, 4, 5, …) digits put next to each other. For example the number after 9 is 10, the number after 99 is 100 and so on. Computers do the same, but they only have 2 digits instead of 10. So counting looks like this: 0, 1, 10, 11, 100, 101, 110, 111 and so on. The other difference between the number system we use and the one computers use is that all of the integer types have a definite size. They only have room for a certain number of digits. So a 4 bit integer might look like this: 0000, 0001, 0010, 0011, 0100. Eventually we run out of space and most computers just wrap around to the beginning. (Which can result in some very strange behavior)

Go's integer types are: uint8, uint16, uint32, uint64, int8, int16, int32 and int64. 8, 16, 32 and 64 tell us how many bits each of the types use. uint means “unsigned integer” while int means “signed integer”. Unsigned integers only contain positive numbers (or zero). In addition there two alias types: byte which is the same as uint8 and rune which is the same as int32. Bytes are an extremely common unit of measurement used on computers (1 byte = 8 bits, 1024 bytes = 1 kilobyte, 1024 kilobytes = 1 megabyte, …) and therefore Go's byte data type is often used in the definition of other types. There are also 3 machine dependent integer types: uint, int and uintptr. They are machine dependent because their size depends on the type of architecture you are using.

Generally if you are working with integers you should just use the int type.

Floating Point Numbers

Floating point numbers are numbers that contain a decimal component (real numbers). (1.234, 123.4, 0.00001234, 12340000) Their actual representation on a computer is fairly complicated and not really necessary in order to know how to use them. So for now we need only keep the following in mind:

Go has two floating point types: float32 and float64 (also often referred to as single precision and double precision respectively) as well as two additional types for representing complex numbers (numbers with imaginary parts): complex64 and complex128. Generally we should stick with float64 when working with floating point numbers.

Example

Let's write an example program using numbers. First create a folder called chapter3 and make a main.go file containing the following:

package main

import "fmt"

func main() {
  fmt.Println("1 + 1 =", 1 + 1)
}

If you run the program and you should see this:

$ go run main.go
1 + 1 = 2

Notice that this program is very similar to the program we wrote in chapter 2. It contains the same package line, the same import line, the same function declaration and uses the same Println function. This time instead of printing the string Hello World we print the string 1 + 1 = followed by the result of the expression 1 + 1. This expression is made up of three parts: the numeric literal 1 (which is of type int), the + operator (which represents addition) and another numeric literal 1. Let's try the same thing using floating point numbers:

 fmt.Println("1 + 1 =", 1.0 + 1.0)

Notice that we use the .0 to tell Go that this is a floating point number instead of an integer. Running this program will give you the same result as before.

In addition to addition Go has several other operators:

+addition
-subtraction
*multiplication
/division
%remainder

Strings

As we saw in chapter 2 a string is a sequence of characters with a definite length used to represent text. Go strings are made up of individual bytes, usually one for each character. (Characters from other languages like Chinese are represented by more than one byte)

String literals can be created using double quotes "Hello World" or back ticks `Hello World`. The difference between these is that double quoted strings cannot contain newlines and they allow special escape sequences. For example \n gets replaced with a newline and \t gets replaced with a tab character.

Several common operations on strings include finding the length of a string: len("Hello World"), accessing an individual character in the string: "Hello World"[1], and concatenating two strings together: "Hello " + "World". Let's modify the program we created earlier to test these out:

package main

import "fmt"

func main() {
  fmt.Println(len("Hello World"))
  fmt.Println("Hello World"[1])
  fmt.Println("Hello " + "World")
}

A few things to notice:

Booleans

A boolean value (named after George Boole) is a special 1 bit integer type used to represent true and false (or on and off). Three logical operators are used with boolean values:

&&and
||or
!not

Here is an example program showing how they can be used:

func main() {
  fmt.Println(true && true)
  fmt.Println(true && false)
  fmt.Println(true || true)
  fmt.Println(true || false)
  fmt.Println(!true)
}

Running this program should give you:

$ go run main.go
true
false
true
true
false

We usually use truth tables to define how these operators work:

ExpressionValue
true && truetrue
true && falsefalse
false && truefalse
false && falsefalse
ExpressionValue
true || truetrue
true || falsetrue
false || truetrue
false || falsefalse
ExpressionValue
!truefalse
!falsetrue

These are the simplest types included with Go and form the foundation from which all later types are built.

Problems

← Previous Index Next →