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.

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

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 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:

Floating point numbers are inexact. Occasionally it is not possible to represent a number. For example computing

`1.01 - 0.99`

results in`0.020000000000000018`

– A number extremely close to what we would expect, but not exactly the same.Like integers floating point numbers have a certain size (32 bit or 64 bit). Using a larger sized floating point number increases it's precision. (how many digits it can represent)

In addition to numbers there are several other values which can be represented: “not a number” (

`NaN`

, for things like`0/0`

) and positive and negative infinity. (`+∞`

and`−∞`

)

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.

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 |

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:

A space is also considered a character, so the string's length is 11 not 10 and the 3rd line has

`"Hello "`

instead of`"Hello"`

.Strings are “indexed” starting at 0 not 1.

`[1]`

gives you the 2nd element not the 1st. Also notice that you see`101`

instead of`e`

when you run this program. This is because the character is represented by a byte (remember a byte is an integer).

One way to think about indexing would be to show it like this instead:`"Hello World"`

. You'd read that as “The string Hello World sub 1,” “The string Hello World at 1” or “The second character of the string Hello World”._{1}Concatenation uses the same symbol as addition. The Go compiler figures out what to do based on the types of the arguments. Since both sides of the

`+`

are strings the compiler assumes you mean concatenation and not addition. (Addition is meaningless for strings)

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:

Expression | Value |

true && true | true |

true && false | false |

false && true | false |

false && false | false |

Expression | Value |

true || true | true |

true || false | true |

false || true | true |

false || false | false |

Expression | Value |

!true | false |

!false | true |

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

How are integers stored on a computer?

We know that (in base 10) the largest 1 digit number is 9 and the largest 2 digit number is 99. Given that in binary the largest 2 digit number is 11 (3), the largest 3 digit number is 111 (7) and the largest 4 digit number is 1111 (15) what's the largest 8 digit number? (hint: 10

^{1}-1 = 9 and 10^{2}-1 = 99)Although overpowered for the task you can use Go as a calculator. Write a program that computes

`32132 × 42452`

and prints it to the terminal. (Use the`*`

operator for multiplication)What is a string? How do you find its length?

What's the value of the expression

`(true && false) || (false && true) || !(false && false)`

?

← Previous | Index | Next → |