[go: up one dir, main page]

100% found this document useful (1 vote)
2K views370 pages

Salinan Head First Go PDF

This document provides an introduction to the Go programming language. It discusses Go's origins at Google where engineers created Go to address issues with long compilation times. The document then summarizes some of Go's key features like fast compilation, garbage collection, and built-in concurrency support. It encourages readers to try out Go and provides instructions on using an online code playground or installing Go on their own computer.

Uploaded by

Fakhri
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
2K views370 pages

Salinan Head First Go PDF

This document provides an introduction to the Go programming language. It discusses Go's origins at Google where engineers created Go to address issues with long compilation times. The document then summarizes some of Go's key features like fast compilation, garbage collection, and built-in concurrency support. It encourages readers to try out Go and provides instructions on using an online code playground or installing Go on their own computer.

Uploaded by

Fakhri
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 370

Playlists

History

Topics

Tutorials

Offers & Deals

Highlights

Settings

Support

Sign Out
1 syntax basics
istory

opics

utorials Let's Get Going


ffers & Deals

ighlights

ettings

Support

Sign Out

Are you ready to turbo-charge your software? Do you want a simple

programming language that compiles fast? That runs fast? That makes it easy to
distribute your work to users? Then you're ready for Go!
Go is a programming language that focuses on simplicity and speed. It's much less
complex than other languages, so it's quicker to learn. And it lets you make full use of
today's multi­core computer processors, so your programs run faster. This chapter will
show you all the Go features that will make your life as a developer easier, and
     
READY, SET, GO!
Back in 2007, the search engine Google had a problem. They had to maintain programs
with millions of lines of code. Before they could test new changes, they had to compile
the code into a runnable form, a process which at the time took the better part of an
hour. Needless to say, this was bad for developer productivity.

So Google engineers Robert Griesemer, Rob Pike, and Ken Thompson sketched out
some goals for a new language:

• Fast compilation

• Less cumbersome code

• Unused memory freed automatically (garbage collection)

• Easy to write software that does several operations simultaneously (concurrency)

• Good support for processors with multiple cores

After a couple years of work, Google had a language that was fast to write code for and
produced programs that were fast to compile and run. The project switched to an open­
source license in 2009. Go is now free for anyone to use. And you should use it! Go is
rapidly gaining popularity thanks to its simplicity and power.

If you're writing a command­line tool, Go can produce executable files for Windows,
Mac, and Linux, all from the same source code. If you're writing a web server, Go can
help you handle many users connecting at once. And no matter what you're writing, it
will help you ensure that your code is easier to maintain and add to.

Ready to learn more? Let's Go!
THE GO PLAYGROUND
The easiest way to try Go is to visit https://play.golang.org in your web browser. There,
the Go team has set up a simple editor where you can enter Go code and run it on their
servers. The result is displayed right there in your browser.

(Of course, this only works if you have a stable Internet connection. If you don't, see
page 25 to learn how to download and run the Go compiler directly on your computer.
Then run the following examples using the compiler instead.)

Let's try it out now!

 Open http://play.golang.org in your browser. (Don't worry if what you see doesn't
quite match the screenshot; it just means they've improved the site since this book was
printed!)
 Delete any code that's in the editing area, and type this instead:

 Click the "Format" button, which will automatically reformat your code according to
Go conventions.

 Click the "Run" button.

You should see "Hello, Go!" displayed at the bottom of the screen. Congratulations,
you've just run your first Go program!

Turn the page, and we'll explain what we just did...

WHAT DOES IT ALL MEAN?


You've just run your first Go program! Now let's look at the code and figure out what it
actually means...

Every Go file starts with a package declaration. A package is a group of code that all
does similar things, like formatting strings or drawing images. The package declaration
gives the name of the package that this file's code will become a part of. In this case, we
use the special package main, which is required if this code is going to be run directly
(usually from the terminal).

Next, Go files almost always have one or more import statements. Each file needs to
import other packages before its code can use the code those other packages contain.
Loading all the Go code on your computer at once would result in a big, slow program,
so instead you specify only the packages you need by importing them.
The last part of every Go file is the actual code, which is often split up into one or more
functions. A function is a group of one or more lines of code that you can call (run)
from other places in your program. When a Go program is run, it looks for a function
named main and runs that first, which is why we named this function main.

Don't worry if you don't understand all this right now!

We'll look at everything in more detail in the next few pages.

The typical Go file layout


You'll quickly get used to seeing these three sections, in this order, in almost every Go
file you work with:

1. The package declaration

2. Any import statements

3. The actual code
The saying goes, "a place for everything, and everything in its place." Go is a very
consistent language. This is a good thing: you'll often find you just know where to look
in your project for a given piece of code, without having to think about it!

THERE ARE NO DUMB QUESTIONS


Q: My other programming language requires that each statement end with
a semicolon. Doesn't Go?

A: You can use semicolons to separate statements in Go, but it's not required (in fact,
it's generally frowned upon).

Q: Why did we run the automatic reformatting tool on our code?

A: Whenever you share your code, other Go developers will expect it to be in the
standard Go format. That means that things like indentation and spacing will be
formatted in a standard way, making it easier for everyone to read. Where other
languages achieve this by relying on people manually reformatting their code to
conform to a style guide, with Go all you have to do is run the standard formatting tool,
and it will automatically fix everything for you. We ran the formatter on every example
we created for this book, and you should run it on all your code, too!

WHAT IF SOMETHING GOES WRONG?


Go programs have to follow certain rules to avoid confusing the compiler. If we break
one of these rules, we'll get an error message.

Suppose we forgot to add parentheses on our call to the Println function on line 6:

If we try to run this version of the program, we get an error:
Go tells us the name of the source code file and the line number where there's a
problem. (The Go Playground saves the contents of the online editor to a temporary file
before running it, which is where the "main. go" file name comes from.) Then it gives a
description of the error. In this case, because we deleted the parenthesis, Go can't tell
we're trying to call the Println function, so it can't understand why we're putting "Hello,
 at the end of line 6.
Go"

BREAKING STUFF IS EDUCATIONAL!

We can get a feel for the rules Go programs have to follow by intentionally breaking our
program in various ways. Take this code sample, try making one of the changes below,
and run it. Then undo your change, and try the next one. See what happens!

If you do this... ...it will fail because...

Delete the package Every Go file has to begin with a package declaration.

declaration...    package
main
Delete the import Every Go file has to import every package it references.

statement...     import"fmt"

Import a second (unused) Go files must import only the packages they reference.

package... imp
ort"fmt" (This helps keep your code compiling fast!)

import"str
ings"

Rename the main Your program looks for a function named m


ainto run

function... f
uncmain
hell
o first.

Change the Pri


ntlncall to Everything in Go is case-sensitive, so although

lower-case... f
mt.P
rin
tlnis valid, there's no such thing as f
mt.pri
ntln
.

fm
t.P
println(
"Hell
o,Go!"
)

Delete the package name The Pr


int
lnfunction isn't part of this package, so Go

before P
rintln
... needs the package name before the function call.

fm
t.Pri
ntln
("Hell
o,Go
!")

Let's try the first one as an example...

CALLING FUNCTIONS
Our example includes a call to the fmt package's Println function. To call a function, type
the function name (Println in this case), and a pair of parenthesis.
Like many functions, Println can take one or more arguments: values you want the
function to work with. The arguments appear in parenthesis after the method name.

Pri
ntl can be called with no arguments, or you can provide several arguments. When
n

we look at other functions later, however, you'll find that most require a specific
number of arguments. If you provide too few or too many, you'll get an error message
saying how many arguments were expected, and you'll need to fix your code.

THE PRINTLN FUNCTION


Use the Println function when you need to see what your program is doing. Any
arguments you pass to it will be printed (displayed) in your terminal, with each
argument separated by a space.

After printing all its arguments, Println will skip to a new terminal line. (That's why "ln"
is at the end of its name.)

USING FUNCTIONS FROM OTHER PACKAGES


The code in our first program is all part of the main package, but the Println function is in
the fmt package. To be able to call Println, we first have to import the package containing
it.
Once we've imported the package, we can access any functions it contains by typing the
package name, a dot, and the name of the function we want.

Here's a code sample that calls functions from a couple other packages. Because we
need to import multiple packages, we switch to an alternate format for the import
statement that lets you list multiple packages within parentheses, one package name
per line.

Once we've importing the math and strings packages, we can access the math package's
F
loo function with math.Floor, and the strings package's Title function with strings.Title.
r

You may have noticed that in spite of including those two function calls in our code, the
above sample doesn't display any output. We'll look at how to fix that next.

FUNCTION RETURN VALUES


In our previous code sample, we tried calling the math.Floor and strings.Title functions,
but they didn't produce any output:
When we call the fmt.Println function, we don't need to communicate with it any further
after that. We pass one or more values for Println to print, and we trust that it printed
them. But sometimes a program needs to be able to call a function and get data back
from it. For this reason, functions in most programming languages can have return
values: a value that the function computes and returns to its caller.

The math.Floor and strings.Title functions are both examples of functions that use return
values. The math.Floor function takes a floating­point number, rounds it down to the
nearest whole number, and returns that whole number. And the strings.Title function
takes a string, capitalizes each word it contains (converting it to "title case"), and
returns the capitalized string.

To actually see the results of these function calls, we need to take their return values
and pass those to fmt.Println:

Once this change is made, the return values get printed, and we can see the results.

POOL PUZZLE
Your job is to take code snippets from the pool and place them into the blank lines in
the code. Don't use the same snippet more than once, and you won’t need to use all the
snippets. Your goal is to make code that will run and produce the output shown.

Answers on page 29.
A GO PROGRAM TEMPLATE
For the code snippets that follow, just imagine inserting them into this full Go program:

Better yet, try typing this program into the Go Playground, and then insert the snippets
one at a time to see for yourself what they do!

STRINGS
We've been passing strings as arguments to Println. A string is a series of bytes that
usually represent text characters. You can define strings directly within your code using
string literals: text between double quotation marks that Go will treat as a string.
Within strings, characters like newlines, tabs, and other characters that would be hard
to include in program code can be represented with escape sequences: a backslash
followed by characters that represent another character. Escape sequence Value

Escape sequence Value

\n A newline character.

\t A tab character.

\" Double quotation marks.

\\ A backslash.

RUNES
Whereas strings are usually used to represent a whole series of text characters, Go's
runes are used to represent single characters.
String literals are written surrounded by double quotation marks ("), but rune literals
are written with single quotation marks (').

Go programs can use any character from any language on earth, because Go uses the
Unicode standard for storing runes. Runes are kept as numeric codes, not the
characters themselves, and if you pass a rune to fmt.Println, you'll see that numeric code
in the output, not the original character.

Just as with string literals, escape sequences can be used in a rune literal to represent
characters that would be hard to include in program code:

BOOLEANS
Boolean values can be one of only two values: true or false. They're especially useful
with conditional statements, which cause sections of code to run only if a condition is
true or false. (We'll look at conditionals in the next chapter.)

NUMBERS
You can also define numbers directly within your code, and it's even simpler than string
literals: just type the number.
As we'll see shortly, Go treats integer and floating­point numbers as different types, so
remember that a decimal point can be used to distinguish between their literal values.

MATH OPERATIONS AND COMPARISONS


Go's basic math operators work just like they do in most other languages. The + symbol
is for addition, - for subtraction, * for multiplication, and / for division..

You can use < and > to compare two values and see if one is less than or greater than
another. You can use == (that's two equals signs) to see if two values are equal, and !=
(that's an exclamation point and an equals sign, read aloud as "not equal") to see if two
values are not equal. The result of a comparison is a boolean value, either true or false.

TYPES
In a previous code sample, we saw the math.Floor function, which rounds a floating­point
number down to the nearest whole number, and the strings.Title function, which
converts a string to title case. It makes sense that you would pass a number as an
argument to the Floor function, and a string as an argument to the Title function. But
what would happen if you passed a string to Floor and a number to Title?
Go prints two error messages, one for each function call, and the program doesn't even
run!

Things in the world around you can often be classified into different types based on
what they can be used for. You don't eat a car or truck for breakfast (because they're
vehicles), and you don't drive an omelet or bowl of cereal to work (because they're
breakfast foods).

Likewise, values in Go are all classified into different types, which specify what the
values can be used for. Integers can be used in math operations, but strings can't.
Strings can be capitalized, but numbers can't. And so on.

Go is statically typed, which means that it knows what the types of your values are
even before your program runs. Functions expect their arguments to be of particular
types, and their return values have types as well (which may or may not be the same as
the argument types). If you accidentally use the wrong type of value in the wrong place,
Go will give you an error message. This is a good thing: it lets you find out there's a
problem before your users do!

Go is statically typed. If you use the wrong type of value in the wrong place,
Go will let you know.

You can view the type of any value by passing it to the reflect package's TypeOf method.
Let's find out what the types are for some of the values we've already seen:
Here's what those types are used for:

Type Description

int An integer. Can be any whole number.

float64 A floating point number. Holds numbers with a fractional part. (The 64in

the type name is because up to 64 bits of data are used to hold the number.

This means that float64values can be fairly, but not infinitely, precise

before being rounded off.)

bool A boolean value. Can only be trueor false


.

string A string. A series of data that usually represents text characters.

Draw lines to match the values below to their types.
Some types will have more than one value that matches with them.

 25                      int

true
 5.2                   float64

  1

false                   bool

 1.0

"hello"                string

Answers on page 29.
DECLARING VARIABLES
In Go, a variable is a piece of storage containing a value. You can give a variable a
name by using a variable declaration. Just use the var keyword followed by the
desired name and the type of values the variable will hold.

Once you declare a variable, you can assign any value of that type to it with = (that's a
single equals sign):

quantity = 2
customerName = "Damon Cole"

You can assign values to multiple variables in the same statement. Just place multiple
variable names on the left side of the =, and the same number of values on the right
side, separated with commas.

Once you've assigned values to variables, you can use them in any context where you
would use the original values:
If you know beforehand what a variable's value will be, you can declare variables and
assign them values on the same line:

You can assign new values to existing variables, but they need to be values of the same
type. Go's static typing ensures you don't accidentally assign the wrong kind of value to
a variable.

Go also requires that every variable you declare get used somewhere in your program. If
you don't, it will report an error, and you'll need to either use the variable or remove its
declaration. This is a good thing, because the presence of an unused variable often
indicates a bug! Go reports an error to help you find and fix the problem.

CODE MAGNETS
A Go program is all scrambled up on the fridge. Can you reconstruct the code snippets
to make a working program that will produce the given output?

Answers on page 29.
SHORT VARIABLE DECLARATIONS
We mentioned that you can declare variables and assign them values on the same line:

But if you know what the initial value of a variable is going to be as soon as you declare
it, it's more typical to use a short variable declaration. Instead of explicitly
declaring the type of the variable and later assigning to it with =, you do both at once
using :=.
Let's update the previous example to use short variable declarations:

There's no need to explicitly declare the variable's type; the type of the value assigned
to the variable becomes the type of that variable.

Because short variable declarations are so convenient and concise, they're used more
often than regular declarations. You'll still see both forms occasionally, though, so it's
important to be familiar with both.

BREAKING STUFF IS EDUCATIONAL!

Take our program that uses variables, try making one of the changes below, and run it.
Then undo your change, and try the next one. See what happens!

If you do this... ...it will fail because...

Add a second You can only declare a variable once. (Although you can

declaration for the assign new values to it as often as you want. You can also
same variable declare other variables with the same name, as long as

they’re in a different scope. We’ll learn about scopes later

in the chapter.)
quantity:
=4

quantity:
=4

Delete the :from a If you forget the :


, it’s treated as an assignment, not a

short variable declaration, and you can’t assign to a variable that hasn’t

declaration been declared.

q
uantity÷=4

Assign a stringto an Variables can only be assigned values of the same type.

intvariable

qu
antit
y:=4
  

q
uantity="a"

Mismatch number of You’re required to provide a value for every variable you’re

variables and values assigning, and a variable for every value.

le
ngth,widt
h:=1
.2

Remove code that All declared variables must be used in your program. If you

uses a variable remove the code that uses a variable, you must also remove

the declaration.

f
mt.
Pri
ntln(
custo
merNam
e)

NAMING RULES
Go has one simple set of rules that apply to the names of variables, functions, and
types:

• A name must begin with a letter, and can have any number of additional letters and
numbers.

• If the name of a variable, function, or type begins with a capital letter, it is considered
exported and can be accessed from packages outside the current one. (This is why the
"P" in fmt.Println is capitalized: so it can be used from the main package or any other.) If a
variable/function/type name begins with a lower­case letter, it is considered
unexported and can only be accessed within the current package.

Those are the only rules enforced by the language. But the Go community follows some
additional conventions as well:

• If a name consists of multiple words, each word after the first should be capitalized,
and they should be attached together without spaces between them, like this: topPrice,
, etc. (The first letter of the name should only be capitalized if you want to
RetryConnection

export it from the package.) This style is often called "camel case" because the
capitalized letters look like the humps on a camel.

• When the meaning of a name is obvious from the context, the Go community's
convention is to abbreviate it: to use i instead of index, max instead of maximum, and so on.
(However, we at Head First believe that nothing is obvious when you're learning a new
language, so we will not be following that convention in this book.)

CONVERSIONS
Math and comparison operations in Go require that the included values be of the same
type. If they're not, you'll get an error when trying to run your code.
The same is true when assigning new values to variables. If the type of value being
assigned doesn't match the declared type of the variable, you'll get an error.

The solution is to use conversions, which let you convert a value from one type to
another type. You just provide the type you want to convert a value to, immediately
followed by the value you want to convert in parentheses.

The result is a new value of the desired type. Here's the result of running TypeOf on an
integer, and again on that same integer after conversion to a float64:

Let's update our failing code example to convert the int value to a float64 before using it
in any math operations or comparisons with other float64 values.
The math operation and comparison both work correctly now!

Now let's try converting an int to a float64 before assigning it to a float64 variable:

Again, with the conversion in place, the assignment is successful.

When making conversions, be aware of how they might change the resulting values. For
example, float64 values can store fractional values, but int values can't. When you
convert a float64 to an int, the fractional portion is truncated (rounded down) to the
nearest whole number! This can throw off any operations you do with the resulting
value.

As long as you're cautious, though, you'll find conversions essential to working with Go.
They allow otherwise­incompatible types to work together.

We've written the Go code below, to calculate a total price with tax and determine if
we have enough funds to make a purchase. But we're getting errors when we try to
include it in a full program!
Fill in the blanks below to update this code. Fix the errors so that it produces the
expected output. (Hint: Before doing math operations or comparisons, you'll need to
use conversions to make the types compatible.)

Answers on page 30.

INSTALLING GO ON YOUR
COMPUTER
The Go Playground is a great way to try out the language. But its practical uses are
limited. You can't use it to work with files, for example. And it doesn't have a way to
take user input from the terminal, which we're going to need for an upcoming program.

So to wrap up this chapter, let's download and install Go on your computer. Don't
worry, the Go team has made it really easy! On most operating systems, you just have to
run an installer program, and you'll be done.
 Visit https://golang.org in your web browser.

 Click the download link.

 Select the binary distribution for your OS. The download should begin
automatically.

 Visit the installation instructions page for your OS (you may be taken there
automatically after the download starts), and follow the directions there. (Don't skip
any steps, or your system may not be able to find the go command!)

 Restart your computer.

 Open a new terminal or command prompt window.

 Confirm Go was installed by typing "goversion" (without quotes) at the prompt and
hitting the Return or Enter key. You should see a message with the version of Go that's
installed.

Web sites are always changing.

It's possible that golang.org or the Go installer will be updated after this book is
published, and these directions will no longer be completely accurate. In that
case, visit:

http://headfirstgo.com

for help and troubleshooting tips!
COMPILING GO CODE
Our interaction with The Go Playground has consisted of typing in code and having it
mysteriously run. Now that we've actually installed Go on your computer, it's time to
take a closer look at how this works.

Computers actually aren't capable of running Go code directly. Before that can happen,
we need to take the source code file and compile it: convert it to a binary format that a
CPU can execute.

Let's try using our new Go installation to compile and run our "Hello, Go!" example
from earlier.

 Using your favorite text editor, save our "Hello, Go!" code from earlier in a plain­
text file named "hello.go".

 Open a new terminal or command prompt window.

 In the terminal, change to the directory where you saved "hello.go".

 Run "gofmthello.go" to clean up the code formatting. (This step isn't required, but
it's a good idea anyway.)
 Run "gobuildhello.go" to compile the source code. This will add an executable file to
the current directory. On Mac or Linux, the executable will be named just "hello". On
Windows, the executable will be named "hello.exe".

 Run the executable file. On Mac or Linux, do this by typing "./hello" (which means
"run a program named hello in the current directory"). On Windows, just type
"hello.exe".

GO TOOLS
When you install Go, it adds an executable named "go" to your command prompt. The go
executable gives you access to various commands, including:

Command Description

gobuild Compiles source code files into executable files, which it will save in

the current directory.

gorun Compiles and runs a source file, but without saving an executable file.

gofmt Re-formats source files using Go standard formatting.


gover
sio
n Displays the current Go version.

We just tried the gofmt command, which re­formats your code in the standard Go
format. It's equivalent to the "Format" button on the Go Playground site. We
recommend running gofmt on every source file you create.

Most editors can be set up to automatically run "go fmt" every time you save a
file! See: https://blog.golang.org/go­fmt­your­code

We also used the gobuild command, which compiles your code into executable files. You
can distribute these files to users, and they'll be able to run them even if they don't have
Go installed.

But we haven't tried the gorun command yet. Let's do that now.

TRY OUT CODE QUICKLY WITH "GO RUN"


The gorun command compiles and runs a source file, without saving an executable file
to the current directory. It's great for quickly trying out simple programs. Let's use it to
run our "hello.go" sample.

 Open a new terminal or command prompt window.

 In the terminal, change to the directory where you saved "hello.go".

 Type "gorunhello.go" and hit Enter/Return. (The command is the same on all
operating systems.)

You'll immediately see the program output. If you make changes to the source code,
there's no need to compile a new executable; just run it with gorun and you'll be able to
see the results right away. When you're working on small programs, gorun is a handy
tool to have!

YOUR GO TOOLBOX

That's it for Chapter 1! You’ve added function calls and types to your
toolbox.

Functions

A function is a chunk of code that you can call from other places in your program.

When calling a function, you can use arguments to provide the function with data.

Go functions can provide data back to their callers using one or more return values.

Types

Values in Go are classified into different types, which specify what the values can be
used for.

Math operations and comparisons between different types are not allowed, but you
can convert a value to a new type if needed.

Go variables can only store values of their declared type.
 BULLET POINTS

 A package is a group of related functions and other code.

 Before you can use a package's functions within a Go file, you need to import that
package.

 A string is a series of bytes that usually represent text characters.

 A rune represents a single text character.

 Go's two most common numeric types are int, which holds integers, and float64,
which holds floating­point decimal numbers.

 The bool type holds boolean values, which are either true or false.

 A variable is a piece of storage that contains values of a specified type.

 A variable, function, or type can only be accessed from code in other packages if its
name begins with a capital letter.

 The gofmt command automatically reformats source files to use Go standard
formatting. You should run gofmt on any code that you plan to share with others.

 The gobuild command compiles Go source code into a binary format that computers
can execute.

 The gorun command compiles and runs source code, but without saving a binary
file.

POOL PUZZLE SOLUTION


Draw lines to match the values below to their types. Some types will have more than
one value that matches with them.

CODE MAGNETS SOLUTION


A Go program is all scrambled up on the fridge. Can you reconstruct the code snippets
to make a working program that will produce the given output?
Fill in the blanks below to update this code. Fix the errors so that it produces the
expected output. (Hint: Before doing math operations or comparisons, you'll need to
use conversions to make the types compatible.)
2 conditionals and loops
History

Topics

Tutorials Which Code Runs Next?


Offers & Deals

Highlights

Settings

Support

Sign Out

Every program has parts that only apply in certain situations.

"This code should run if there's an error. Otherwise, that other code should run."
Almost every program contains code that should be run only when a certain condition
is true. So almost every programming language provides conditional statements
that let you determine whether to run segments of code. Go is no exception.
You may also need some parts of your code to run repeatedly. Like most languages, Go
provides loops that run sections of code more than once. We'll learn to use both
conditionals and loops in this chapter!
CALLING METHODS
In Go, it's possible to define methods: functions that are associated with values of a
given type. Go methods are kind of like the methods that you may have seen attached to
"objects" in other languages, but they're much simpler.

We'll be taking a detailed look at how methods work in chapter TODO. But we need to
use a couple methods to make our examples for this chapter work, so let's look at some
brief examples of calling methods now.

The time package has a Time type that represents a date (year, month, and day) and time
(hour, minute, second, etc.). Each time.Time value has a Year method that returns the
year. The code below uses this method to print the current year:

The time.Now function returns a new Time value for the current date and time, which we
store in the now variable. Then, we call the Year method on the value that now refers to:

The Year method returns an integer with the year, which we then print.

The strings package has a Replacer type that can search through a string for a substring,
and replace each occurrence of that substring with another string. The code below
replaces every # symbol in a string with the letter "o":
The strings.NewReplacer function takes arguments with a string to replace ("#"), and a
string to replace it with ("o"), and returns a strings.Replacer value. When we pass a string
to the Replacer value's Replace method, it returns a string with those replacements made.

Whereas the functions we saw earlier belonged to a package, the methods belong to an
individual value. That value is what appears to the left of the dot.

MAKING THE GRADE


In this chapter, we're going to look at features of Go that let you decide whether to run
some code or not, based on a condition. Let's look at a situation where we might need
that ability...

We need to write a program that allows a student to type in their percentage grade, and
tells them whether they passed or not. Passing or failing follows a simple formula: a
grade of 60% or more is passing, less than 60% is failing. So our program will need to
give one response if the entered percentage grade is 60 or greater, and a different
response otherwise.

COMMENTS
Let's create a new file, "pass_fail.go", to hold our program. We're going to take care of a
detail we omitted in our previous programs, and add a description of what the program
does at the top.

Most Go programs include descriptions in their source code of what they do, intended
for people maintaining the program to read. These comments are ignored by the
compiler.

The most common form of comment is marked with two slash characters (//).
Everything from the slashes to the end of the line is treated as part of the comment. A //
comment can appear on a line by itself, or following a line of code.

// The total number of widgets in the system.
var TotalCount int // Can only be a whole number.

The less frequently used form of comments, block comments, spans multiple lines.
Block comments start with /*, end with */, and everything between those markers
(including newlines) is part of the comment.

/*
Package widget includes all the functions used
for processing widgets.
*/

GETTING A GRADE FROM THE USER


Now let's add some actual code to our pass_fail.go program. The first thing it needs to do
is allow the user to input a percentage grade. We want them to type a number and press
Enter, and we'll store the number they typed in a variable. Let's add code to handle this.

First, we need to let the user know to enter something, so we use the fmt.Print function
to display a prompt. (Unlike the Println function, Print doesn't skip to a new terminal
line after printing a message, which lets us keep the prompt and the user's entry on the
same line.)

Next, we need a way to read (receive and store) input from the program's "standard
input", which all keyboard input goes to. The line reader:=bufio.NewReader(os.Stdin)
stores a bufio.Reader value in the reader variable that can do that for us.

To actually get the user's input, we call the ReadString method on the Reader. ReadString
requires an argument with a rune (character) that marks the end of the input. We want
to read everything the user types up until they press Enter, so we give ReadString a
newline rune.

Once we have the user input, we simply print it.

That's the plan, anyway. But if we try to compile or run this program, we'll get an error:
Don't worry too much about the details of how bufio.Reader works.

All you really need to know at this point is that it lets us read input from the
keyboard.

MULTIPLE RETURN VALUES FROM A FUNCTION OR


METHOD
We're trying to read the user's keyboard input, but we're getting an error. The compiler
is reporting a problem in this line of code:

The problem is that the ReadString method is trying to return two values, and we've only
provided one variable to assign a value to.

In most programming languages, functions and methods can only have a single return
value, but in Go, they can return any number of values. The most common use of
multiple return values in Go is to return an additional error value that can be consulted
to find out if anything went wrong while the function or method was running. A few
examples:
Go requires that every variable that gets declared must also get used, somewhere in
your program. If we add an error variable and then don't check it, our code won't
compile. Unused variables often indicate a bug, so this is an example of Go helping you
detect and fix bugs!

OPTION 1: IGNORE THE ERROR RETURN VALUE


WITH THE BLANK IDENTIFIER
The ReadString method returns a second error value along with the user's input, and we
need to do something with the second value. We've tried just adding a second variable
and ignoring it, but our code still won't compile.
When we have a value that would normally be assigned to a variable, but that we don't
intend to use, we can use Go's blank identifier. Assigning a value to the blank
identifier essentially discards it (while making it clear to others reading your code that
you are doing so). To use the blank identifier, simply type a single underscore ( _ )
character in an assignment statement, where you would normally type a variable name.

Let's try using the blank identifier in place of our old error variable:

Now to try the change out. In your terminal, change to the directory where you saved
"pass_fail.go", and run the program with:

go run pass_fail.go

When you type a grade (or any other string) at the prompt and press enter, your entry
will be echoed back to you. Our program is working!

OPTION 2: HANDLE THE ERROR


That's true. If an error actually occurred, this program wouldn't
tell us!

If we got an error back from the ReadString method, the blank identifier would just cause
the error to be ignored, and our program would proceed anyway, possibly with invalid
data.

In this case, it would be more appropriate to alert the user and stop the program if
there was an error.

The log package has a Fatal function that can do both of these operations for us at once:
log a message to the terminal and stop the program. ("Fatal" in this context means
reporting an error that "kills" your program.)

Let's get rid of the blank identifier, and replace it with an error variable so that we're
recording the error again. Then, we'll use the Fatal function to log the error and halt the
program.
But if we try running this updated program, we'll see there's a new problem...

CONDITIONALS
If our program encounters a problem reading input from the keyboard, we've set it up
to report the error and stop running. But now, it stops running even when everything's
working correctly!

Functions and methods like ReadString return an error value of nil, which basically
means "there's nothing there". In other words, if error is nil, it means there was no
error. But our program is set up to simply report the nil error! What we should do is
exit the program only if the error variable has a value other than nil.

We can do this using conditionals: statements that cause a block of code (one or more
statements surrounded by {} braces) to be executed only if a condition is met.
An expression is evaluated, and if its result is true, the code in the conditional block
body is executed. If it's false, the conditional block is skipped.

if true {                                 if false {
       fmt.Println("I'll be printed!")           fmt.Println("I won't!"
}                                         }

As with most other languages, Go supports multiple branches in the condition. These
statements take the form if/elseif/else.

if score == 100 {
       fmt.Println("Perfect!")
} else if score >= 60 {
       fmt.Println("You pass.")
} else {
       fmt.Println("You fail!")
}

Conditionals rely on a boolean expression (one that evaluates to true or false) to decide
whether the code they contain should be executed.

if 1 == 1 {                               if 1 >= 2 {
       fmt.Println("I'll be printed!")           fmt.Println("I won't!")
}                                         }

if 1 > 2 {                                 if 2 <= 2 {
       fmt.Println("I won't!")                    fmt.Println("I'll be printed!")
}                                          }

if 1 < 2 {                                 if 2 != 2 {
       fmt.Println("I'll be printed!")            fmt.Println("I won't!")
}                                          }

When you need to execute code only if a condition is false, you can use !, the boolean
negation operator, which lets you take a true value and make it false, or a false value and
make it true.

if !true {                                 if !false {
       fmt.Println("I won't be printed!")         fmt.Println("I will!")
}                                          }

If you want to run some code only if two conditions are both true, you can use the &&
("and") operator. If you want it to run if either of two conditions is true, you can use the
 ("or") operator.
||

if true && true {                          if false || true {
       fmt.Println("I'll be printed!")            fmt.Println("I'll be printed!")
}                                          }

if true && false {                         if false || false {
       fmt.Println("I won't!")                    fmt.Println("I won't!")
}                                          }

THERE ARE NO DUMB QUESTIONS


Q: My other programming language requires that an if statement's
condition be surrounded with parentheses. Doesn't Go?

A: No, and in fact the gofmt tool will remove any parentheses you add, unless you're
using them to set order of operations.

Because they're in conditional blocks, only some of the Println calls in the below code
will be executed. Write down what the output would be.

(We’ve done the first two lines for you.)
Answers on page 42.

LOGGING A FATAL ERROR,


CONDITIONALLY
Our grading program is reporting an error and exiting, even if it reads input from the
keyboard successfully.
We know that if the value in our error variable is nil, it means reading from the
keyboard was successful. Now that we know about if statements, let's try updating our
code to log an error and exit only if error is not nil.

If we re­run our program, we'll see that it's working again. And now, if there are any
errors when reading user input, we'll see those as well!

CODE MAGNETS

A Go program that prints the size of a file is on the fridge. It calls the os.Stat function,
which returns an os.FileInfo value, and possibly an error value. Then it calls the Size
method on the FileInfo value to get the file size.
But the original program uses the _ blank identifier to ignore the error value from
os. . If an error occurs (which could happen if the file doesn't exist), this will cause
Stat

the program to fail.

Reconstruct the extra code snippets to make a program that works just like the original
one, but also checks for an error from os.Stat. If the error from os.Stat is not nil, the
error should be reported, and the program should exit. Discard the magnet with the _
blank identifier; it won't be used in the finished program.

Answers on page 43.
CONVERTING STRINGS TO NUMBERS
Conditional statements will also let us evaluate the entered grade. Let's add an if/else
statement to determine whether the grade is passing or failing. If the entered
percentage grade is 60 or greater, we'll set the status to "passing". Otherwise, we'll set it
to "failing".

// package and import statements omitted
func main() {
       fmt.Print("Enter a grade: ")
       reader := bufio.NewReader(os.Stdin)
       input, error := reader.ReadString('\n')
       if error != nil {
              log.Fatal(error)
       }

       if input >= 60 {
              status := "passing"
       } else {
              status := "failing"
       }
}

In its current form, though, this gets us a compilation error.

Here's the problem: input from the keyboard is read in as a string. Go can only compare
to other numbers; we can't compare a number with a string. And there's no direct type
conversion from string to a number:

There are a pair of issues we'll need to address here:

• The input string still has a newline character on the end, from when the user pressed
the Enter key while entering it. We need to strip that off.

• The remainder of the string needs to be converted to a floating­point number.

Removing the newline character from the end of the input string will be easy. The strings
package has a TrimSpace function that will remove all whitespace characters (newlines,
tabs, and regular spaces) from the start and end of a string.

So, we can get rid of the newline on input by passing it to TrimSpace, and assigning the
return value back to the input variable.
input = strings.TrimSpace(input)

All that should remain in the input string now is the number the user entered. We can
use the strconv package's ParseFloat function to convert it to a float64 value.

You pass ParseFloat a string that you want to convert to a number, as well as the number
of bits of precision the result should have. Since we're converting to a float64 value, we
pass the number 64. (In addition to float64, Go offers a less­precise float32 type, but you
shouldn't use that unless you have a good reason.)

 converts the string to a number, and returns it as a float64 value. Like
ParseFloat

, it also returns a second error value, which will be nil unless there was some
ReadString

problem converting the string. (For example, a string that can't be converted to a
number. We don't know of a numeric equivalent to "hello"...)

This whole "bits of precision" thing isn't that important right now.

It's basically just a measure of how much computer memory a floating­point
number takes up. As long as you know that you want a float64, and so you
should pass 64 as the second argument to ParseFloat, you'll be fine.

Let's update pass_fail.go with calls to TrimSpace and ParseFloat:
First, we add the appropriate packages to the import section. We add code to remove the
newline character from the input string. Then we pass input to ParseFloat, and store the
resulting float64 value in a new variable, grade.

Just as we did with ReadString, we test whether ParseFloat returns an error value. If it
does, we report it and stop the program.

Finally, we update the conditional statement to test the number in grade, rather than the
string in input. That should fix the error stemming from comparing a string to a
number.

If we try to run the updated program, we no longer get the "mismatchedtypesstringand
" error. So it looks like we've fixed that issue. But we've got a couple more errors to
int

address. We'll look at those next.

BLOCKS
We've converted the user's grade input to a float64 value, and added it to a conditional
to determine if it's passing or failing. But we're getting a couple more compile errors:

As we've seen previously, declaring a variable like status without using it afterwards is
an error in Go. It seems a little strange that we're getting the error twice, but let's
disregard that for now. We'll add a call to Println to print the percentage grade we were
given, and the value of status.

But now we get a new error, saying that the status variable is undefined when we
attempt to use it in our Println statement! What's going on?

Go code can be divided up into blocks, segments of code. Blocks are usually
surrounded by curly braces ({}), although there are also blocks at the source code file
and package levels. Blocks can be nested inside one another.

The bodies of functions and conditionals are both blocks as well. Understanding this
will be key to solving our problem with the status variable...
BLOCKS AND VARIABLE SCOPE
Each variable you declare has a scope: a portion of your code that it's "visible" within.
A declared variable can be accessed anywhere within its scope, but if you try to access it
outside that scope, you'll get an error.

A variable's scope consists of the block it's declared in, and any blocks nested within
that block.

Here are the scopes of the variables in the code above:

• The scope of packageVar is the entire main package. You can access packageVar anywhere
within any function you define in the package.

• The scope of functionVar is the entire function it's declared in, including the if block
nested within that function.

• The scope of conditionalVar is limited to the if block. When we try to access
 after the closing } brace of the if block, we'll get an error saying that
conditionalVar

 is undefined!
conditionalVar

Now that we understand variable scope, we can explain why our status variable was
undefined in the grading program. We declared status in our conditional blocks. (In
fact, we declared it twice, since there are two separate blocks. That's why we got two
"statusdeclaredandnotused" errors.) But then we tried to access status outside those
blocks, where it was no longer in scope.
The solution is to move the declaration of the status variable out of the conditional
blocks, and up to the function block. Once we do that, the status variable will be in scope
both within the nested conditional blocks, and at the end of the function block.

Don't forget to change the short variable declarations within the
nested blocks to assignment statements!

If you don't change both occurrences of := to =, you'll accidentally create new
variables named status within the nested conditional blocks, which will then
be out of scope at the end of the enclosing function block!

WE'VE FINISHED THE GRADING PROGRAM!


That was it! Our pass_fail.go program is ready for action! Let's take one more look at the
complete code:
You can try running the finished program as many times as you like. Enter a percentage
grade under 60, and it will report a failing status. Enter a grade over 60, and it will
report that it's passing. Looks like everything's working!

Some of the lines of code below will result in a compile error, because they refer to a
variable that is out of scope. Cross out the lines that have errors.
Answers on page 44.

LET'S BUILD A GAME


We're going to wrap up this chapter by building a simple game. If that sounds daunting,
don't worry; you've already learned most of the skills you're going to need! Along the
way, we'll learn about loops, which will allow the player to take multiple turns.

Let's look at everything we'll need to do:

This example debuted in Head First Ruby. (Another fine book that you should
also buy!) It worked so well that we're using it again here.
Let's create a new source file, named "guess.go".

It looks like our first requirement is to generate a random number. Let's get started!

PACKAGE NAMES VS. IMPORT PATHS


The math/rand package has a Intn function that can generate a random number for us, so
we'll need to import math/rand. Then we'll call rand.Intn to generate the random number.
One is the package's import path, and the other is the package's name.

When we say math/rand we're referring to the package's import path, not its name. An
import path is just a unique string that identifies a package, that you use in an import
statement. Once you've imported the package, then you can refer to it by its package
name.

For every package we've used so far, the import path has been identical to the package
name. Here are a few examples:

Import Path Package Name

"fmt" f
mt

"log" l
og

"strin
gs" s
trings

But the import path and package name don't have to be identical. Many Go packages
fall into similar categories, like compression or complex math. So they're grouped
together under similar import path prefixes, such as "archive/" or "math/". (Think of them
as being similar to the paths of directories on your hard drive.)
Import Path Package Name

"
archi
ve" a
rchi
ve

"
archi
ve/ta
r" t
ar

"
archi
ve/zi
p" z
ip

"
math" m
ath

"
math/
cmplx
" c
mplx

"
math/
rand" r
and

The Go language doesn't require that a package name have anything to do with its
import path. But by convention, the last (or only) segment of the import path is also
used as the package name. So if the import path is "archive", the package name will be
ar , and if the import path is "archive/zip", the package name will be zip.
chive

Import Path Package Name

"
archi
ve" a
rchi
ve

"
arch
ive/
tar
" t
ar

"a
rch
ive/
zip
" z
ip

"
math
" m
ath
"
math/
cmplx
" c
mplx

"
math
/ra
nd" rand

So, that's why our import statement uses a path of "math/rand", but our main function just
uses the package name: rand.

GENERATING A RANDOM NUMBER


Pass a number to rand.Intn, and it will return a random integer between 0 and the
number you provided. In other words, if we pass an argument of 100, we'll get a random
number in the range 0­99. Since we need a number in the range 1­100, we'll just add 1
to whatever random value we get. We'll store the result in a variable, target. We'll do
more with target later, but for now we'll just print it.

If we try running our program right now, we'll get a random number. But we just get
the same random number over and over! The problem is, random numbers generated
by computers aren't really that random. But there's a way increase that randomness...
To get different random numbers, we're going to need to pass a value to the rand.Seed
function. That will "seed" the random number generator, that is, give it a value that it
will use to generate other random values. But if we keep giving it the same seed value, it
will keep giving us the same random values, and we'll be back where we started.

We saw earlier that the time.Now function will give us a Time value representing the
current date and time. We can use that to get a different seed value every time we run
our program.

The rand.Seed function expects an integer, so we can't pass it a Time value directly.
Instead, we call the Unix method on the Time, which will convert it to an integer.
(Specifically, it will convert it to Unix time format, which is an integer with the number
of seconds since January 1, 1970. But you don't really need to remember that.) We pass
that integer to rand.Seed.

We also add a couple Println calls to let the user know we've chosen a random number.
But aside from that, we can leave the rest of our code, including the call torand.Intn, as­
is. Seeding the generator should be the only change we need to make.

Now, each time we run our program, we'll see our message, along with a random
number. It looks like our changes are successful!
GETTING AN INTEGER FROM THE KEYBOARD
Our first requirement is complete! Next we need to get the user's guess via the
keyboard.

That should work in much the same way as when we read in a percentage grade from
the keyboard for our grading program.

There will be only one difference: instead of converting the input to a float64, we need to
convert it to an int (since our guessing game uses only whole numbers). So we'll pass
the string read from the keyboard to the strconv package's Atoi (string to integer)
function instead of its ParseFloat function. Atoi will give us an integer as its return value.
(Just like ParseFloat, Atoi might also give us an error if it can't convert the string. If that
happens, we again report the error and exit.)
COMPARING THE GUESS TO THE TARGET
Another requirement finished. And this next one will be easy... We just need to
compare the user's guess to the randomly generated number, and tell them whether it
was higher or lower.

If guess is less than target, we need to print a message saying the guess was "low".
Otherwise, if guess is greater than target, we should print a message saying the guess was
"high". Sounds like we need an if/elseif statement. We'll add it below the other code in
our main function.
Now try running our updated program from the terminal. It's still set up to print target
each time it runs, which will be useful for debugging. Just enter a number lower than
t
arge, and you should be told your guess was "low". If you re­run the program, you'll
t

get a new target value. Enter a number higher than that and you'll be told your guess
was "high".

LOOPS
Another requirement down! Let's look at the next one.

Currently, the player only gets to guess once, but we need to allow them to guess up to
10 times.

The code to prompt for a guess is already in place. We just need to run it more than
once. We can use a loop to execute a block of code repeatedly. If you've used other
programming languages, you've probably encountered loops. When you need one or
more statements executed over and over, you place them inside a loop.

Loops always begin with the for keyword. In one common kind of loop, for is followed
by three segments of code that control the loop:

• An init statement that is usually used to initialize a variable

• A condition expression that determines when to break out of the loop

• A post statement that runs after each iteration of the loop

Often, the init statement is used to initialize a variable, the condition expression keeps
the loop running until that variable reaches a certain value, and the post statement is
used to update the value of that variable. For example, in this snippet, the t variable is
initialized to 3, the condition keeps the loop going while t>0, and the post statement
subtracts 1 from t each time the loop runs. Eventually, t reaches 0 and the loop ends.

The ++ and -- statements are frequently used in loop post statements. Each time they're
evaluated, ++ adds 1 to a variable's value, and -- subtracts 1.
Used in a loop, ++ and -- are convenient for counting up or down.

Go also includes the assignment operators += and -=. They take the value in a variable,
add or subtract another value, and then assign the result back to the variable.

 and -= can be used in a loop to count in increments other than 1.
+=

When the loop finishes, execution will resume with whatever statement follows the loop
block. But the loop will keep going as long as the condition expression evaluates to true.
It's possible to abuse this; here are a loop that will run forever, and a loop that will
never run at all:

It's possible for a loop to run forever, in which case your program will
never stop on its own.

If this happens, with the terminal active, hold the Control key and press C to
halt your program.
INIT AND POST STATEMENTS ARE OPTIONAL
If you want, you can leave out the init and post statements from a for loop, leaving only
the condition expression. (Although you still need to make sure the condition
eventually evaluates to false, or you could have an infinite loop on your hands.)

LOOPS AND SCOPE


Just like with conditionals, the scope of any variables declared within a loop's block is
limited to that block. (Although the init statement, condition expression, and post
statement can be considered part of that scope as well.)

Also as with conditionals, any variable declared before the loop will still be in scope
within the loop's control statements and block, and will still be in scope after the loop
exits.

BREAKING STUFF IS EDUCATIONAL!


Here's a program that uses a loop to count to 3. Try making one of the changes below,
and run it. Then undo your change, and try the next one. See what happens!

If you do this... ...it will break because...

Add parentheses Some other languages require parentheses around a forloop’s

after the for control statements, but not only does Go not require them, it

keyword doesn’t allow them.

for(x:=1;x<=3;

x++)

Delete the :from Unless you’re assigning to a variable that’s already been

the init statement declared in the enclosing scope (which you usually won’t be),

the init statement needs to be a declaration, not an assignment.


=1

Remove the =from The expression x<3becomes falsewhen xreaches 3(whereas

the condition x<=3would still be true


). So the loop would only count to 2
.

expression

x<3

Reverse the Because the condition is already false when the loop begins (x

comparison in the is initialized to 1


, which is less than 3
), the loop will never run.

condition

expression
x>
=3

Change the post The xvariable will start counting down from 1(1
, 0
, -1
, -2
, etc.),

statement from x++ and since it will never be greater than 3


, the loop will never

to x-
- end.

x--

Move the Variables declared in the init statement or within the loop

fmt.Pr
intln
(x) block are only in scope within the loop block.

statement outside

the loop’s block

Look carefully at the init statement, condition expression, and post statement for
each of these loops. Then write what you think the output will be for each one.

(We've done the first one for you.)
Answers on page 45.

USING A LOOP IN OUR GUESSING


GAME
Our game still only prompts the user for a guess once. Let's add a loop around the code
that prompts the user for a guess and tells them if it was "low" or "high", so that the
user can guess 10 times.

We'll use an int variable named guesses to track the number of guesses the player has
made. In our loop's init statement, we'll initialize guesses to 0. We'll add 1 to guesses with
each iteration of the loop, and we'll stop the loop when guesses reaches 10.

We'll also add a Println statement at the top of the loop's block to tell the user how many
guesses they have left.

Now that our loop is in place, if we run our game again, we'll get asked 10 times what
our guess is!
Since the code to prompt for a guess and state whether it was "high" or "low" is inside
the loop, it gets run repeatedly. After 10 guesses, the loop (and the game) will end.

But the loop always runs 10 times, even if the player guesses correctly! Fixing that will
be our next requirement.

SKIPPING PARTS OF A LOOP WITH "CONTINUE"


AND "BREAK"
The hard part is done! We only have a couple requirements left to go.

Right now, the loop that prompts the user for a guess always runs 10 times. Even if the
player guesses correctly, we don't tell them so, and we don't stop the loop. Our next task
is to fix that.

Go provides two keywords that control the flow of a loop. The first, continue,
immediately skips to the next iteration of a loop, without running any further code in
the loop block.
In the above example, the string "aftercontinue" never gets printed, because the continue
keyword always skips back to the top of the loop before the second call to Println can be
run.

The second keyword, break, immediately breaks out of a loop. No further code within the
loop block is executed, and no further iterations are run. Execution moves to the first
statement following the loop.

Here, in the first iteration of the loop, the string "beforebreak" gets printed, but then the
b
r ea statement immediately breaks out of the loop, without printing the "afterbreak"
k

string, and without running the loop again (even though it normally would have run
two more times). Execution instead moves to the statement following the loop.

The break keyword seems like it would be applicable to our current problem: we need to
break out of our loop when the player guesses correctly. Let's try using it in our game...

BREAKING OUT OF OUR GUESSING LOOP


We're using an if/elseif conditional to tell the player the status of their guess. If the
player guesses a number too "high" or too "low", we currently print a message telling
them so.

It stands to reason that if the guess is neither too high nor too low, it must be correct.
So let's add an else branch onto the conditional, that will run in the event of a correct
guess. Inside the block for the else branch, we'll tell the player they were right, and then
use the break statement to stop the guessing loop.
Now, when we make a correct guess, we'll see a congratulatory message, and the loop
will exit without repeating the full 10 times.

That's another requirement complete!

REVEALING THE TARGET


We're so close! Just one more requirement left!

If the player makes 10 guesses without finding the target number, the loop will exit. In
that event, we need to print a message saying they lost, and tell them what the target
was.

But we also exit the loop if the player guesses correctly. We don't want to say the player
has lost when they've already won!
So, before our guessing loop, we'll declare a success variable that holds a bool. (We need
to declare it before the loop so that it's still in scope after the loop ends.) We'll initialize
suc  to a default value of false. Then, if the player guesses correctly, we'll set success to
cess

tru
e, indicating we don't need to print the failure message.

After the loop, we add an if block that prints the failure message. But an if block only
runs if its condition evaluates to true, and we only want to print the failure message if
 is false. So we add the boolean negation operator, !. As we saw earlier, ! turns
success

 values false and false values true.
true

The result is that the failure message won't be printed if success is false, but will be
printed if success is true.

THE FINISHING TOUCHES


Congratulations, that's the last requirement!

Let's take care of a couple final issues with our code, and then try out our game!

First, as we mentioned, it's typical to add a comment at the top of each Go program
describing what it does. Let's add one now.
Our program is also encouraging cheaters by printing the target number at the start of
every game. Let's remove the Println call that does that.

We should finally be ready to try running our complete code!

First, we'll run out of guesses on purpose to ensure the target number gets displayed...

Then we'll try guessing successfully. Our game is working great!

CONGRATULATIONS, YOUR GAME IS COMPLETE!


Using conditionals and loops, you've written a complete game in Go! Pour yourself a
cold drink — you've earned it!

Here's our complete guess.go source code!
YOUR GO TOOLBOX

That's it for Chapter 2! You’ve added conditionals and loops to your
toolbox.
 BULLET POINTS

 A method is a kind of function that's associated with values of a given type.

 Go treats everything from a // marker to the end of the line as a comment—and
ignores it.

 Multi­line comments start with /* and end wit */. Everything in between, including
newlines, is ignored.

 It's conventional to include a comment at the top of every program, explaining what
it does.

 Unlike most programming languages, Go allows multiple return values from a
function or method call.
 One common use of multiple return values is to return the function's main result,
and then a second value indicating whether there was an error.

 To discard a value without using it, use the _ blank identifier. The blank identifier
can be used in place of any variable in any assignment statement.

 Functions, conditionals, and loops all have blocks of code that appear within {}
braces.

 The code doesn't appear within {} braces, but files and packages also comprise
blocks.

 The scope of a variable is limited to the block it is defined within, and all blocks
nested within that block.

 In addition to a name, a package may have an import path that is required when
importing it.

 The continue keyword skips to the next iteration of a loop.

 The break keyword exits out of a loop entirely.

Because they're in conditional blocks, only some of the Println calls in the below code
will be executed. Write down what the output would be.
CODE MAGNETS SOLUTION

A Go program that prints the size of a file is on the fridge. It calls the os.Stat function,
which returns an os.FileInfo value, and possibly an error. Then it calls the Size method
on the FileInfo value to get the file size.

The original program used the _ blank identifier to ignore the error value from os.Stat. If
an error occurred (which could happen if the file doesn't exist), this would cause the
program to fail.

Your job was to reconstruct the extra code snippets to make a program that works just
like the original one, but also checks for an error from os.Stat. If the error from os.Stat is
not nil, the error should be reported, and the program should exit.

Some of the lines of code below will result in a compile error, because they refer to a
variable that is out of scope. Cross out the lines that have errors.
Look carefully at the init statement, condition expression, and post statement for
each of these loops. Then write what you think the output will be for each one.
3 functions
History

Topics

Tutorials Call Me
Offers & Deals

Highlights

Settings

Support

Sign Out

You've been missing out. You've been calling functions like a pro. But the only

functions you could call were the ones Go defined for you. Now, it's your turn. We're
going to show you how to create your own functions. We'll learn how to declare
functions with and without parameters. We'll declare functions that return a single
value, and we'll learn how to return multiple values so that we can indicate when there's
been an error. And we'll learn about pointers, which allow us to make more memory­
efficient function calls.

SOME REPET I T I V E C O DE
Suppose we need to calculate the amount of paint needed to cover several walls. The
manufacturer says each liter of paint covers 10 square meters. So, we'll need to multiply
each wall's width (in meters) by its height to get its area, and then divide that by 10 to
get the number of liters of paint needed.

This works, but it has a couple problems:

• The calculations seem to be off by a tiny fraction, and are printing oddly precise
floating­point values. We really only need a couple decimal places of precision.

• There's a fair amount of repeated code, even now. This will get worse as we add more
walls.

Both items will take a little explanation to address, so let's just look at the first issue for
now...

The calculations are slightly off because ordinary floating­point arithmetic on
computers is ever­so­slightly inaccurate. (Usually by a few quadrillionths.) The reasons
are a little too complicated to get into here, but this problem isn't exclusive to Go.

But as long as we round the numbers to a reasonable degree of precision before
displaying them, we should be fine. Let's take a brief detour to look at a function that
will help us do that.
FORMATTING OUTPUT WITH PRINTF AND SPRINTF

Floating­point numbers in Go are kept with a high degree of precision. This can be
cumbersome when you want to display them:

To deal with these sorts of formatting issues, the fmt package provides the Printf
function. Printf stands for "print, with formatting". It takes a string, and inserts one or
more values into it, formatted in specific ways. Then it prints the resulting string.

The Sprintf function (also part of the fmt package) works just like Printf, except that it
returns a formatted string instead of printing it.

It looks like Printf and Sprintf can help us limit our displayed values to the correct
number of places. The question is, how? To be able to use this method effectively, we’ll
need to learn about two features of Printf:

1. Formatting verbs (the %0.2f in the strings above is a verb)

2. Value widths (that’s the 0.2 in the middle of the verb)
We’ll explain exactly what those arguments to Printf mean on the next
few pages.

We know, those method calls above look a little confusing. We'll show you a
ton of examples that should clear that confusion up.

FORMATTING VERBS

The first argument to Printf is a string that will be used to format the output. Most of it
is formatted exactly as it appears in the string. Any percent signs (%), however, will be
treated as the start of a formatting verb, a section of the string that will be
substituted with a value in a particular format. The remaining arguments are used as
values with those verbs.

The letter following the percent sign indicates which verb to use. The most common
verbs are:

Verb Output

%
f Floating-point number
%
d Decimal integer

%
s String

%
t Boolean (tr
ueor f
als
e)

%
v Any value (chooses an appropriate format based on the supplied value’s type)

%
#v Any value, formatted as it would appear in Go program code

%
T Type of the supplied value (i
nt, strin
g, etc.)

%
% A literal percent sign

Notice, by the way, that we are making sure to add a newline at the end of each
formatting string using the \n escape sequence. This is because unlike Println, Printf
does not automatically add a newline for us.

We want to point out the %#v formatting verb in particular. Because it prints values the
way they would appear in Go code, rather than how they normally appear, %#v can show
you some values that would otherwise be hidden in your output. In this code, for
example, %#v reveals an empty string, a tab character, and a newline, all of which were
invisible when printed with %v. We'll use %#v more later in the book!
FORMATTING VALUE WIDTHS
So the %f formatting verb is for floating­point numbers. We can use %f in our program to
format the amount of paint needed.

It looks like our value is being rounded to a reasonable number. But it's still showing six
places after the decimal point, which is really too much for our current purpose.

For situations like this, formatting verbs let you specify the width of the formatted
value.

Let’s say we want to format some data in a plain­text table. We need to ensure the
formatted value fills a minimum number of spaces, so that the columns align properly.

You can specify the minimum width after the percent sign in a format sequence. If the
argument for that format sequence is shorter than the minimum width, it will be
padded with spaces until the minimum width is reached.
FORMATTING FRACTIONAL NUMBER WIDTHS

And now we come to the part that’s important for today’s task: you can use value widths
to specify the precision (the number of displayed digits) for floating­point numbers.
Here’s the format:

The minimum width of the entire number includes decimal places. If it’s included,
shorter numbers will be padded with spaces at the start until this width is reached. If
it’s omitted, no spaces will ever be added.

The width after the decimal point is the maximum number of digits to show. If a more
precise number is given, it will be rounded (up or down) to fit in the given number of
decimal places.

Here’s a quick demonstration of various width values in action:

That last format, "%.2f", will let us take floating­point numbers of any precision and
round them to two decimal places. (It also won’t do any unnecessary padding.) Let's try
it with the overly­precise values from our program to calculate paint volumes.

That's much more readable. It looks like the Printf function can format our numbers for
us. Let's get back to our paint calculator program, and apply what we've learned there.

USING PRINTF IN OUR PAINT CALCULATOR


Now we have a Printf verb, "%.2f", that will let us round a floating­point number to two
decimal places. Let's update our paint quantity calculation program to use it.
At last, we have reasonable­looking output! The tiny imprecisions introduced by
floating­point arithmetic have been rounded away.

Good point. Go lets us declare our own functions, so perhaps we should
move this code into a function.

As we mentioned way back at the start of Chapter 1, a function is a group of one or more
lines of code that you can call from other places in your program. And our program has
two groups of lines that look very similar:
Let's see if we can convert these two sections of code into a single function.

DECLARING FUNCTIONS
A simple function declaration might look like this:

A declaration begins with the func keyword, followed by the name you want the function
to have, a pair of parentheses (), and then a block containing the function's code.

Once you've declared a function, you can call it elsewhere in your package simply by
typing its name, followed by a pair of parentheses. When you do, the code in the
function's block will be run.

Notice that when we call sayHi, we're not typing the package name and a dot before the
function name. When you call a function that's defined in the current package, you
should not specify the package name. (Typing main.sayHi() would result in a compile
error.)
The rules for function names are the same as the rules for variable names:

• A name must begin with a letter, followed by any number of additional letters and
numbers. (You'll get a compile error if you break this rule.)

• Functions whose name begins with a capital letter are exported, and can be used
outside the current package. If you only need to use a function inside the current
package, you should start its name with a lower­case letter.

• Names with multiple words should use

DECLARING FUNCTION PARAMETERS


If you want calls to your method to include arguments, you'll need to declare one or
more parameters. A parameter is a variable local to a function, whose value is set
when the function is called.

You can declare one or more parameters between the parentheses in the function
declaration, separated by commas. As with any variable, you'll need to provide a name
followed by a type (float64, bool, etc.) for each parameter you declare.

If a function has parameters defined, then you'll need to pass a matching set of
arguments when calling it. When the function is run, each parameter will be set to a
copy of the value in the corresponding argument. Those parameter values are then used
within the code in the function block.

USING FUNCTIONS IN OUR PAINT CALCULATOR


Now that we know how to declare our own functions, let's see if we can get rid of the
repetition in our paint calculator.

We'll move the code to calculate the amount of paint to a function, named paintNeeded.
We'll get rid of the separate width and height variables, and instead take those as function
parameters. Then, in our main function, we'll just call paintNeeded for each wall we need to
paint.
No more repeated code, and if we want to calculate the paint needed for additional
walls, we just add more calls to paintNeeded. This is much cleaner!

FUNCTIONS AND VARIABLE SCOPE


Our paintNeeded function declares an area variable within its function block:

As with conditional and loop blocks, variables declared within a function block are only
in scope within that function block. So if we were to try to access the area variable
outside of the paintNeeded function, we'd get a compile error:

But, also as with conditional and loop blocks, variables declared outside a function
block will be in scope within that block. That means we can declare a variable at
package level, and access it within any function in that package.
Below is a program that declares several functions, then calls those functions within
. Write down what the program output would be.
main

(We've done the first line for you.)

Answers on page 33.
FUNCTION RETURN VALUES
Suppose we wanted to total the amount of paint needed for all the walls we're going to
paint. We can't do that with our current paintNeeded function; it just prints the amount,
and then discards it!

So instead, let's revise the paintNeeded function to return a value. Then, whoever calls it
can print the amount, do additional calculations with it, or whatever else they need.

Functions always return values of a specific type (and only that type). To declare that a
function returns a value, add the type of that return value following the parameters in
the function declaration. Then use the return keyword in the function block, followed by
the value you want to return.

Callers of the function can then assign the return value to a variable, pass it directly to
another function, or whatever else they need to do with it.

When a return statement runs, the function exits immediately, without running any
code that follows it. You can use this together with an if statement to exit the function
in conditions where there's no point in running the remaining code (due to an error or
some other condition).
That means that it's possible to have code that never runs under any circumstances, if
you include a return statement that isn't part of an if block. This almost certainly
indicates a bug in the code, so Go helps you detect this situation by requiring that any
function that declares a return type must end with a return statement. Ending with any
other statement will cause a compile error.

You'll also get a compile error if the type of your return value doesn't match the
declared return type.

USING A RETURN VALUE IN OUR PAINT


CALCULATOR
Now that we know how to use function return values, let's see if we can update our
paint program to print the total amount of paint needed in addition to the amount
needed for each wall.

We'll update the paintNeeded function to return the amount needed. We'll use that return
value in the main function, both to print the amount for the current wall, and to add to a
tota
l variable that tracks the total amount of paint needed.
It works! Returning the value allowed our main function to decide what to do with the
calculated amount, rather than relying on the paintNeeded function to print it.

BREAKING STUFF IS EDUCATIONAL!

Here's our updated version of the paintNeeded function that returns a value. Try making
one of the changes below, and try to compile it. Then undo your change, and try the
next one. See what happens!

       area := width * height
       return area / 10.0
}

If you do this... ...it will break because...

Remove the return If your function declares a return type, Go requires

statement: that it include a returnstatement.

fu
ncp
aintN
eeded(widt
h,

he
ightfloa
t64)float
64{

  
    
 area:=width*

he
ight
 
     
 retu
rnar
ea/10
.0

Add a line after the re


tur
n If your function declares a return type, Go requires

statement: that its last statement be a returnstatement.

funcp
aintN
eeded(widt
h,

heightfloa
t64)float
64{

      
 area:=width*

height

      
 retu
rnarea/10.
0

      
 fmt.
Println(ar
ea/

10.0)

Remove the return type Go doesn’t allow you to return a value you haven’t

declaration: declared.

funcp
aintN
eeded(widt
h,

heightfloa 64{
t64)float

 
    
  are
a:=w
idth*

h
eigh
t

 
    
  ret
urnar
ea/1
0.0

Go requires that the type of the returned value match


Change the type of value
the declared type.
being returned:

f
uncp
aint
Needed
(widt
h,

h
eigh
tflo
at64)float
64{

 
    
   ar
ea:=width*

h
eigh
t

 
    
   re
turni
nt(ar
ea/

1
0.0)

}
THE PAINTNEEDED FUNCTION NEEDS ERROR
CHECKING

It looks like the paintNeeded function had no idea the argument passed to it was invalid. It
went right ahead and used that invalid argument in its calculations, and returned an
invalid result. This is a problem — even if you knew a store where you could purchase a
negative number of liters of paint, would you really want to apply that to your house?
We need a way of detecting invalid arguments and reporting an error.

In Chapter 2, we saw a couple different functions that, in addition to their main return
value, also return a second value indicating whether there was an error. The strconv.Atoi
function, for example, attempted to convert a string to an integer. If the conversion was
successful, it returned an error value of nil, meaning our program could proceed. But if
the error value wasn't nil, it meant the string couldn't be converted to a number. In that
event, we chose to print the error value and exit the program.

If we want to do the same for the paintNeeded function, we're going to need two things:

• The ability to create a value representing an error

• The ability to return an additional value from paintNeeded

Let's get started figuring this out!
ERROR VALUES
Before we can return an error value from our paintNeeded function, we need an error
value to return. An error value is basically any value with a method named Error that
returns a string. The simplest way to create one is to pass a string to the errors package's
 function, which will return a new error value. If you call the Error method on that
New

error value, you'll get the string you passed to errors.New.

But if you're passing the error value to a function in the fmt or log packages, you
probably don't need to call its Error method. Functions in fmt and log have been written
to check whether the values passed to them have Error methods, and print the return
value of Error if they do.

If you need to format numbers or other values for use in your error message, you can
use the fmt.Errorf function. It inserts values into a format string just like fmt.Printf or
fmt.Spr
i nt, but instead of printing or returning a string, it returns an error value.
f

DECLARING MULTIPLE RETURN VALUES


Now we need a way to specify that our paintNeeded function will return an error value
along with the amount of paint needed.

To declare multiple return values for a function, place the return value types in a second
set of parentheses in the function declaration (after the parentheses for the function
parameters), separated with commas. (The parentheses around the return values are
optional when there's only one return value, but are required if there's more than one
return value.)

From then on, when calling that function, you'll need to account for the additional
return values, usually by assigning them to additional variables.

If it makes the purpose of the return values clearer, you can supply names for each
return value, similar to parameter names. The main purpose of named return values is
as documentation for programmers reading the code.

USING MULTIPLE RETURN VALUES WITH OUR


PAINTNEEDED FUNCTION
As we saw on the previous page, it's possible to return multiple values of any type. But
the most common use for multiple return values is to return a primary return value,
followed by an additional value indicating whether the function encountered an error.
The additional value is usually set to nil if there were no problems, or an error value if
an error occurred.

We'll follow that convention with our paintNeeded function as well. We'll declare that it
returns two values, a float64 and an error. (Error values have a type of error.) The first
thing we'll do in the function block is to check whether the parameters are valid. If
either the width or height parameters are less than 0, we'll return a paint amount of 0
(which is meaningless, but we do have to return something), and an error value that we
generate by calling fmt.Errorf. Checking for errors at the start of the function allows us to
easily skip the rest of the function's code by calling return if there's a problem.

If there were no problems with the parameters, we proceed to calculate and return the
paint amount just like before. The only other difference in the function code is that we
return a second value of nil along with the paint amount, to indicate there were no
errors.

In the main function, we add a second variable to record the error value from paintNeeded.
We print the error (if any), and then print the paint amount.

If we pass an invalid argument to paintNeeded, we'll get an error return value, and print
that error. But we also get 0 as the amount of paint. (As we said, this value is
meaningless when there's an error, but we had to use something for the first return
value.) So we wind up printing the message "0.00 liters needed"! We'll need to fix that...
ALWAYS CHECK FOR ERRORS!
When we pass an invalid argument to paintNeeded, we get an error value back, which we
print for the user to see. But we also get an (invalid) amount of paint, which we print as
well!

When a function returns an error value, it usually has to return a primary return value
as well. But any other return values that accompany an error value should be
considered unreliable, and ignored.

When you call a function that returns an error value, it's important to test whether that
value is nil before proceeding. If it's anything other than nil, it means there's an error
which must be handled.

How the error should be handled depends on the situation. In the case of our paintNeeded
function, it might be best to simply skip the current calculation and proceed with the
rest of the program:

But since this is such a short program, you could instead call log.Fatal to display the
error message and exit the program.

The important thing to remember is that you should always check the return values to
see whether there is an error. What you do with the error at that point is up to you!

BREAKING STUFF IS EDUCATIONAL!

Here's a program that calculates the square root of a number. But if a negative number
is passed to the squareRoot function, it will return an error value. Make one of the
changes below, and try to compile it. Then undo your change, and try the next one. See
what happens!

package main

import (
       "fmt"
       "math"
)

func squareRoot(number float64) (float64, error) {
       if number < 0 {
              return 0, fmt.Errorf("can't get square root of negative number")
       }
       return math.Sqrt(number), nil
}

func main() {
       root, err := squareRoot(­9.3)
       if err != nil {
              fmt.Println(err)
       } else {
              fmt.Printf("%0.3f", root)
       }
}

If you do this... ...it will break because...

Remove one of the The number of arguments to returnmust always


arguments to return
: match the number of return values in the function

r
eturnmath
.Sqrt(numb
er)
,ni
l declaration.

Remove one of the variables If you use any of the return values from a function,

the return values are Go requires you to use all of them.

assigned to:

root r:=squareRoo
,er t(-9
.3)

Remove the code that uses Go requires that you use every variable you declare.

one of the return values: This is actually a really useful feature when it comes

r
oot,err:
=squ
areRo
ot(
-9.3
) to error return values, because it helps keep you from

i
fer
r!=n
il{ accidentally ignoring an error.

      
   fm
t.Println(
err
)

}else{

 
     
     
fmt.
Printf
("%
0.3f
",

r
oot)

POOL PUZZLE

Your job is to take code snippets from the pool and place them into the blank lines in
the code. Don't use the same snippet more than once, and you won’t need to use all the
snippets. Your goal is to make code that will run and produce the output shown.
Answers on page 34.
FUNCTION PARAMETERS RECEIVE
COPIES OF THE ARGUMENTS
As we mentioned, when you call a function that has parameters declared, you need to
provide arguments to the call. The value in each argument is copied to the
corresponding parameter variable. (Programming languages that do this are sometimes
called "pass­by­value".)

Go is a "pass­by­value" language; function parameters receive a copy of the
arguments from the function call.

This is fine in most cases. But if you want to pass a variable's value to a function and
have it change the value in some way, you'll run into trouble. The function can only
change the copy of the value in its parameter, not the original. So any changes you
make within the function won't be visible outside it!

Here's an updated version of the double function we showed earlier. It takes a number,
multiplies it by 2, and prints the result.

Suppose we wanted to move the statement that prints the doubled value from the double
function back to the function that calls it, though. It won't work, because double only
alters its copy of the value. Back in the calling function, when we try to print, we'll get
the original value, not the doubled one!

We need a way to allow a function to alter the original value a variable holds, rather
than a copy. To learn how to do that, we'll need to make one more detour away from
functions, to learn about pointers. Go on a Detour

POINTERS

You can get the address of a variable using & (an ampersand), which is Go's "address of"
operator. For example, this code initializes a variable, prints its value, and then prints
the variable's address...
We can get addresses for variables of any type. Notice that the address differs for each
variable.

And what are these "addresses", exactly? Well, if you want to find a particular house in
a crowded city, you use its address...

Just like a city, the memory your computer sets aside for your program is a crowded
place. It's full of variable values: booleans, integers, strings, and more. Just like the
address of a house, if you have the address of a variable, you can use it to find the value
that variable contains.

Values that represent the address of a variable are known as pointers, because they
point to the location where the variable can be found.

POINTER TYPES
The type of a pointer is written with a * symbol, followed by the type of the variable the
pointer points to. The type of a pointer to an int variable, for example, would be written
*in (you can read that aloud as "pointer to int").
t

We can use the reflect.TypeOf function to show us the types of our pointers from the
previous program:

We can declare variables that hold pointers. A pointer variable can only hold pointers to
one type of value, so a variable might only hold *int pointers, only *float64 pointers, and
so on.

As with other types, if you'll be assigning a value to the pointer variable right away, you
can use a short variable declaration instead:

GETTING OR CHANGING THE VALUE AT A POINTER


You can get the value of the variable a pointer refers to by typing the * operator right
before the pointer in your code. To get the value at myIntPointer, for example, you'd type
*myIntP
o inte
r. (There's no official consensus on how to read * aloud, but we like to
pronounce it as "value at": *myIntPointer is "value at myIntPointer".)

The * operator can also be used to update the value at a pointer:

In the code above, *myIntPointer=8 accesses the variable at myIntPointer (that is, the myInt
variable) and assigns a new value to it. So not only is the value of *myIntPointer updated,
but myInt is as well.

CODE MAGNETS
A Go program that uses a pointer variable is scrambled up on the fridge. Can you
reconstruct the code snippets to make a working program that will produce the given
output?

The program should declare myInt as an integer variable, and myIntPointer as a variable
that holds an integer pointer. Then it should assign a value to myInt, and assign a pointer
to myInt as the value of myIntPointer. Finally, it should print the value at myIntPointer.
USING POINTERS WITH FUNCTIONS Answers on page 34.

It's possible to return pointers from functions; just declare that the function's return
type is a pointer type.

(By the way, unlike some other languages, it's okay to return a pointer to a variable
that's local to a function. Even though that variable is no longer in scope, as long as you
still have the pointer, Go will ensure you can still access the value.)

You can also pass pointers to functions as arguments. Just specify that the type of one
or more parameters should be a pointer.

Make sure you only use pointers as arguments, if that's what the function declares it
will take. If you try to pass a value directly to a function that's expecting a pointer, you'll
get a compile error.

Now you know the basics of using pointers in Go. We're ready to end our detour, and
get back to fixing our double function!
FIXING OUR "DOUBLE" FUNCTION USING
POINTERS
We have a double function that takes an int value and multiplies it by 2. We want to be
able to pass a value in, and have that value doubled. But, as we learned, Go is a pass­by­
value language, meaning that function parameters receive a copy of any arguments
from the caller. Our function is doubling its copy of the value, and leaving the original
untouched!

Here's where our detour to learn about pointers is going to be useful. If we pass a
pointer to the function, and then alter the value at that pointer, the changes will still be
effective outside the function!

We only need to make a few small changes to get this working. In the double function, we
need to update the type of the number parameter to take a *int rather than an int. Then
we'll need to change the function code to update the value at the number pointer, rather
than updating a variable directly. Finally, in the main function, we just need to update
our call to double to pass a pointer rather than a direct value.

When we run this updated code, a pointer to the amount variable will be passed to the
doubl function. The double function will take the value at that pointer, and double it,
e

thereby changing the value in the amount variable. When we return to the main function
and print the amount variable, we'll see our doubled value!

You've learned a lot about writing your own functions in this chapter. The benefits of
some of these features may not be clear right now. Don't worry, as our programs get
more complex in later chapters, we'll be making good use of everything you've learned!

We've written the negate function below, which is supposed to update the value of the
 variable to its opposite (false), and update the value of the lies variable to its
truth

opposite (true). But when we call negate on the truth and lies variables and then print
their values, we see that they're unchanged!

Fill in the blanks below so that negate takes a pointer to a boolean value instead of
taking a boolean value directly, then updates the value at that pointer to the opposite
value. Be sure to change the calls to negate to pass a pointer instead of passing the
value directly!
Answers on page 34.

YOUR GO TOOLBOX

That's it for Chapter 3! You’ve added function declarations and pointers to
your toolbox.

 BULLET POINTS

 The fmt.Printf and fmt.Sprintf functions print values with formatting. The first
argument should be a formatting string containing verbs (%d, %f, %s, etc.) that values
will be substituted for.

 Within a formatting verb, you can include a width: a minimum number of
characters the formatted value will take up. For example, %12s results in a 12­
character string (padded with spaces), %02d results in a 2­character integer (padded
with zeros), and %0.3f results in a floating point number rounded to 3 decimal places.

 If you want calls to your function to accept arguments, you must declare one or
more parameters, including types for each, in the function declaration. The number
and type of arguments must always match the number and type of parameters, or
you'll get a compile error.

 If you want your function to return one or more values, you must declare the return
value types in the function declaration.

 You can't access a variable declared within a function outside that function. But you
can access a variable declared outside a function (usually at package level) within
that function.

 When a function returns multiple values, the last value usually has a type of error.
Error values have an Error() method that returns a string describing the error.

 By convention, functions return an error value of nil to indicate there are no errors.

 You can access the value a pointer holds by putting a * right before it: *myPointer

 If a function receives a pointer as a parameter, and it updates the value at that
pointer, then the updated value will still be visible outside the function.

Below is a program that declares several functions, then calls those functions within
mai. Write down what the program output would be.
n
POOL PUZZLE SOLUTION

CODE MAGNETS SOLUTION


Playlists

4 packages
History

Topics

Tutorials Bundles of Code


Offers & Deals

Highlights

Settings

Support

Sign Out

It's time to get organized. So far, we've been throwing all our code together in

a single file. As our programs grow bigger and more complex, that's going to quickly
become a mess.
In this chapter, we'll show you how to create your own packages to help keep related
code together in one place. But packages are good for more than just organization.
Packages are an easy way to share code between your programs. And they're an easy
way to share code with other developers.

.
DIFFERENT PROGRAM S , SAME FUNCTION
We've written two programs, each with an identical copy of a function, and it's
becoming a maintenance headache...

On this page, we've got a new version of our pass_fail.go program from Chapter 2. The
code that reads a grade from the keyboard has been moved to a new getFloat function.
get
Floa
t returns the floating­point number the user typed, unless there's an error, in
which case it returns 0 and an error value. If an error is returned, the program reports it
and exists, otherwise it reports whether the grade is passing or failing, as before.

On this page, we've got a new tocelsius.go program, that lets the user type a temperature
in the Fahrenheit measurement system and converts it to the Celsius system.

Notice that the getFloat function in tocelsius.go is identical to the getFloat function in
.
pass_fail.go
SHARING CODE BETWEEN PROGRAMS USING
PACKAGES
Actually, there is something we can do — we can move the shared function
to a new package!

Go allows us to define our own packages. As we discussed back in Chapter 1, a package
is a group of code that all does similar things. The fmt package formats output, the math
package works with numbers, the strings package works with strings, and so on. We've
used the functions from each of these packages in multiple programs already.

Being able to use the same code between programs is one of the major reasons packages
exist. If parts of your code are shared between multiple programs, you should consider
moving them into packages.

THE GO WORKSPACE DIRECTORY HOLDS PACKAGE


CODE
Go tools look for package code in a special directory (folder) on your computer called
the workspace. By default, the workspace is a directory named go in the current user's
home directory.

The workspace directory contains three subdirectories:

• bin, which holds compiled binary executable programs. (We'll talk about bin more later
in the chapter.)

• pkg, which holds compiled binary package files. (We'll also talk about pkg more later in
the chapter.)

• src, which holds Go source code.

Within src, code for each package lives in its own separate subdirectory. By convention,
the subdirectory name should be the same as the package name (so code for a gizmo
package would go in a gizmo subdirectory).

Each package directory should contain one or more source code files. The file names
don't matter, but they should end in a .go extension.

THERE ARE NO DUMB QUESTIONS


Q: You said a package folder can contain multiple files. What should go in
each file?

A: Whatever you want! You can keep all of a package's code in one file, or split it
between multiple files. Either way, it will all become part of the same package.

CREATING A NEW PACKAGE


Let's try setting up a package of our own in the workspace. We'll make a simple package
named greeting that prints greetings in various languages.

The workspace directory isn't created by default when Go is installed, so you'll need to
create it yourself. Start by going to your home directory. (The path is C:\Users\yourname on
most Windows systems, /Users/yourname on Macs, and /home/yourname on most Linux
systems.) Within the home directory, create a directory named go — this will be our new
workspace directory. Within the go directory, create a directory named src.

Finally, we need a directory to hold our package code. By convention, a package's
directory should have the same name as a package. Since our package will be named
gre
etin
g, that's the name you should use for the directory.

We know, that seems like a lot of nested directories (and actually, we'll be nesting them
even deeper shortly). But trust us, once you've built up a collection of packages of your
own as well as packages from others, this structure will help you keep your code
organized.

And more importantly, this structure helps Go tools find the code. Because it's always
in the src directory, Go tools know exactly where to look to find code for the packages
you're importing.

Your next step is to create a file within the greeting directory, and name it greeting.go.
The file should include the code below. We'll talk about it more shortly, but for now
there's just a couple things we want you to notice...

Like all of our Go source code files thus far, this file starts with a package line. But unlike
the others, this code isn't part of the main package; it's part of a package named greeting.

Also notice the two function definitions. They aren't much different from other
functions we've seen so far. But because we want these functions to be accessible
outside the greeting package, notice that we capitalize the first letter of their names so
the functions are exported.
IMPORTING OUR PACKAGE INTO A PROGRAM
Now let's try using our new package within a program.

In your workspace directory, within the src subdirectory, create another subdirectory
named hi. (We don't have to store code for executable programs within the workspace,
but it's a good idea.)

Then, within your new hi directory, we need to create another source file. We can name
the file anything we want, as long as it ends with a .go extension, but since this is going
to be an executable command, we'll name it command.go. Save the code below within the
file.

Like in every Go source code file, this code starts with a package line. But because we
intend this to be an executable command, we need to use a package name of main.
Generally, the package name should match the name of the directory it's kept in, but
the main package is an exception to that rule.
Next we need to import the greeting package so we can use its functions. Go tools look
for package code in a folder within the workspace's src directory whose name matches
the name in the import statement. To tell Go to look for code in the src/greeting directory
within the workspace, we use import"greeting".

Finally, because this is code for an executable, we need a main function that will be called
when the program runs. In main we call both functions that are defined in the greeting
package. Both calls are preceded by the package name and a dot, so that Go knows
which package the functions are a part of.

We're all set; let's try running the program. In your terminal or command prompt
window, use the cd command to change to the src/hi directory within your workspace
directory. (The path will vary based on the location of your home directory.) Then, use
 to run the program.
goruncommand.go

When it sees the import"greeting" line, Go will look in the greeting directory in your
workspace's src directory for the package source code. That code gets compiled and
imported, and we're able to call the greeting package's methods!

PACKAGES USE THE SAME FILE LAYOUT


Remember back in Chapter 1, we talked about the three sections every Go source code
file has?
That rule holds true for the main package in our command.go file, of course. In our code, you
can see a package declaration, followed by an imports section, followed by the actual code
for our package.

Packages other than main follow the same format. You can see that our greeting.go file
also has a package declaration, imports section, and the actual package code at the end.

BREAKING STUFF IS EDUCATIONAL!

Take our code for the greeting package, as well as the code for the program that imports
it. Try making one of the changes below, and run it. Then undo your change, and try the
next one. See what happens!
If you do this... ...it will fail because...

Change the name The Go tools use the name in the import path as the name of

on the greeting the directory to load the package source code from. If they

directory don’t match, the code won’t load.

Change the name The contents of the greetingdirectory will actually load, as a

on the packageline package named salutation


. Since the function calls in command.go

of greeting.go still reference the greetingpackage, though, we’ll get errors.

packagesalutation

Change the Functions whose names begin with a lower-case letter are

function names in unexported, meaning they can only be used within their own

greeting.goand package. To use a function from a different package, its name

command.goto all must begin with a capital letter, so it’s exported.

lower-case

funcH
hello()       

func
H
hi()  
    
     
 

greetin
g.H
hello()

greeti
ng.
Hhi
()      

POOL PUZZLE
Your job is to take code snippets from the pool and place them into the blank lines.
Don't use the same snippet more than once, and you won’t need to use all the snippets.
Your goal is to set up a calc package within a Go workspace so calc's functions can be
used within command.go.

Answers on page 34.
PACKAGE NAMING CONVENTIONS
Developers using a package are going to need to type its name each and every time they
call a function from that package. (Think of fmt.Printf, fmt.Println, fmt.Print, etc.) To
make that as painless as possible, there are a few rules package names should follow:

• A package name should be all lower­case.

• The name should be abbreviated if the meaning is fairly obvious (such as fmt).

• It should be one word, if possible. If two words are needed, they should not be
separated by underscores, and the second word should not be capitalized. (The strconv
package is one example.)

• Imported package names can conflict with local variable names, so don't use a name
that package users are likely to want to use as well. (For example, if the strings package
were named string, no one who imported that package would be able to name a local
variable string).

PACKAGE QUALIFIERS
When accessing a function, variable, etc. that's exported from a different package, you
need to qualify the name of the function or variable by typing the package name before
it. When you access a function or variable that's defined in the current package,
however, you should not qualify the package name.

In our command.go file, since our code is in the main package, we need to specify that the
Hel
lo and Hi functions are from the greeting package, by typing greeting.Hello and
gre
etin
g. .
Hi
Suppose that we called the Hello and Hi methods from another function in the greeting
package, though. There, we would just type Hello and Hi (without the package name
qualifier) because we are calling the functions from the same package where they're
defined.

MOVING OUR SHARED CODE TO A PACKAGE


Now that we understand how to add packages to the Go workspace, we're finally ready
to move our getFloat function to a package that our pass_fail.go and tocelsius.go programs
can both use.

Let's name our package keyboard, since it reads user input from the keyboard. We'll start
by creating a new directory named keyboard inside our workspace's src directory.

Next, we'll create a source code file within the keyboard directory. We can name it
anything we want, but we'll just name it after the package: keyboard.go.

At the top of the file, we'll need a package declaration with the package name: keyboard.
Then, because this is a separate file, we'll need an import statement with all the packages
used in our code: bufio, os, strconv, and strings. (We need to leave out the fmt and log
packages, as those are only used in the pass_fail.go and tocelsius.go files.)

Finally, we can copy the code from the old getFloat function as­is. But we need to be sure
to rename the function to GetFloat, because it won't be exported unless the first letter of
its name is capitalized.

Now the pass_fail.go program can be updated to use our new keyboard package.

Because we're removing the old getFloat function, we need to remove the unused bufio,
o, strconv, and strings imports. In their place, we'll import the new keyboard package.
s

In our main function, in place of the old call to getFloat, we'll call the new keyboard.GetFloat
function. The rest of the code is unchanged.
If we run the updated program, we'll see the same output as before.

We can make the same updates to the tocelsius.go program.

We update the imports, remove the old getFloat, and call keyboard.GetFloat instead.

And again, if we run the updated program, we'll get the same output as before. But this
time, instead of relying on redundant function code, we're using the shared function in
our new package!

NESTED PACKAGE DIRECTORIES AND IMPORT


PATHS
When you're working with the packages that come with Go, like fmt and strconv, the
package name is usually the same as its import path (the string you use in an import
statement to import the package). But as we saw in Chapter 2, that's not always the
case...

Some sets of packages are grouped together by import path prefixes like "archive/" and
. We said to think of these prefixes as being similar to the paths of directories on
"math/"

your hard drive... And that wasn't a coincidence. These import path prefixes are created
using directories!

You can nest groups of similar packages together in a directory in your Go workspace.
That directory then becomes part of the import path for all the packages it contains.

Suppose, for example, that we wanted to add packages for greetings in additional
languages. That would quickly become a mess if we placed them all directly in the src
directory. But if we place the new packages under the greeting directory, they'll all be
grouped neatly together.

And placing the packages under the greeting directory affects their import path, too. If
the dansk package were stored directly under src, its import path would be "dansk". But
place it within the greeting directory, and its import path becomes "greeting/dansk". Move
the deutsch package under the greeting directory, and its import path becomes
. The original greeting package will still be available at an import path of
"greeting/deutsch"

, as long as its source code file is stored directly under the greeting directory
"greeting"

(not a subdirectory).
Suppose that we had a deutsch package nested under our greeting package directory, and
that its code looked like this:

Let's update our hi/command.go code to use the deutsch package as well. Since it's nested
under the greeting directory, we'll need to use an import path of "greeting/deutsch". But
once it's imported, we'll be using just the package name to refer to it: deutsch.
As before, we can run our code by using the cd command to change to the src/hi
directory within your workspace directory. Then, we can use goruncommand.go to run the
program. We'll see the results of our calls to the deutsch package methods in the output.

INSTALLING PACKAGES WITH "GO INSTALL"


When we use gorun, Go has to find and compile all the packages a program depends on
before it can execute the program. But it throws those compiled packages away when
it's done.

Since Go's compilation is so fast, this isn't much of a problem on small projects. But
once you find yourself writing many packages for a single project, you may find yourself
waiting many seconds for everything to compile. This can add up when you're just
testing small changes.

You can speed up this process with the goinstall command. The goinstall command
saves compiled code as binary files (that is, files that aren't readable by humans but are
easy for a computer to read) in your Go workspace. Once a package is installed with go
, it won't be compiled again unless you change its source code.
install

Let's try installing the greeting package now. From a terminal, type goinstall, followed
by a space, then the import path of the package you want to install (greeting). It doesn't
matter what directory you do this from; the go tool will look up the package source code
within your workspace based on its import path.
If it doesn't show any error messages, and just returns you to your system prompt, that
means everything worked correctly.

Now, if you look in your workspace directory, you'll see a new pkg directory has been
created. Inside that will be a folder for indicating your operating system and type of
CPU. And inside that will be a binary file for your compiled package.

There's no need to manually change the pkg folder or any of its contents; the go tool will
manage them for you. And the next time you compile or run a program that uses your
installed package, the pre­compiled version will be used automatically!

INSTALLING PROGRAM EXECUTABLES WITH "GO


INSTALL"
The goinstall command can save compiled versions of executable programs, too. Just
give it the name of a directory within src that contains code for an executable program
(that is, .go files that begin with packagemain). The program will be compiled and an
executable (a file you can execute even without Go installed) will be stored in a bin
directory in your Go workspace.

Let's try installing an executable for our hi/command.go program. As before, from a
terminal, we type goinstall, a space, and the name of a folder within our src directory
(hi). Again, it doesn't matter what directory you do this from; the go tool will look the
directory up within the src directory.
When Go sees that the file inside the hi directory contains a packagemain declaration, it
will know this is code for an executable program. First, if the packages the program
depends on aren't already installed to the pkg directory, it will compile and install them.
Then it will compile an executable file for the program itself, storing it in a directory
named bin in the Go workspace. (The bin directory will be created automatically if it
doesn't already exist.)

Now, you can use the cd command to change to the bin directory within your Go
workspace. Once you're in bin, you can run the executable by typing ./hi (or hi.exe on
Windows).

CHANGING WORKSPACES WITH THE GOPATH


ENVIRONMENT VARIABLE
You may see developers on various websites talking about "setting your GOPATH" when
discussing the Go workspace. GOPATH is an environment variable that Go tools consult to
find the location of your workspace. You can use GOPATH to move your workspace to a
different directory, if you like.

An environment variable lets you store and retrieve values, kind of like a Go
variable, but it's maintained by the operating system, not by Go. You can configure
some programs by setting environment variables, and that includes the Go tool.

Suppose that, instead of in your home directory, you had set up your greeting package
inside a directory named code in the root of your hard drive. And now you want to run
your command.go file, which depends on greeting.

But you're getting an error saying the greeting package can't be found, because the go
tool is still looking in the go directory in your home directory:

SETTING GOPATH
If your code is stored in a directory other than the default, you'll need to configure the
 tool to look in the right place. You can do that by setting the GOPATH environment
go

variable. How you'll do that depends on your operating system.

On Mac or Linux systems:


You'll need to use the export command to set the environment variable. At a terminal
prompt, type:

export GOPATH="/code"

For a directory named code in the root of your hard drive, you'll want to use a path of
"/code". You can substitute a different path if your code is in a different location.

On Windows systems:
On Windows systems:
You'll need to use the set command to set the environment variable. At a command
prompt, type:

set GOPATH="C:\code"

For a directory named code in the root of your hard drive, you'll want to use a path of
"C:\code". You can substitute a different path if your code is in a different location.

Once that's done, gorun (and other Go tools) should immediately begin using the
directory you specified as their workspace. That means the greeting library will be found,
and the program will run!

Note that the methods above will only set GOPATH for the current terminal/command
prompt window. You'll need to set it again for each new window you open. But there are
ways to set an environment variable permanently, if you want. The methods differ for
each operating system, so we won't have space to go into them here. If you type
"environment variables" followed by the name of your OS into your favorite search
engine, the results should include helpful instructions.

PUBLISHING PACKAGES
We're getting so much use out of our keyboard package, we wonder if others might find it
useful, too.
Let's create a repository to hold our code on GitHub, a popular code sharing website.
That way, other developers can download it and use it in their own projects! Our
GitHub username is headfirstgo, and we'll name the repository keyboard, so its URL will
be:

htt
ps:/
/gith
ub.co
m/h
eadf
irs
tgo
/key
board

We'll upload the keyboard.go file to the repository, without

Hmm, that's a valid concern. There can only be one keyboard directory in the Go
workspace's src directory, and so it looks like we can only have one package named
key
boar
d!
Let's try that: we'll move our package into a directory structure that represents the URL
where it's hosted. Inside our src directory, we'll create another directory named
. Inside that, we'll create a directory named after the next segment of the URL,
github.com

. And then we'll move our keyboard package directory from the src directory
headfirstgo

into the headfirstgo directory.

Although moving the package into a new subdirectory will change its import path, it
won't change the package name. And since the package itself only contains references
to the name, we don't have to make any changes to the package code!

We will need to update the programs that rely on our package, though, because the
package import path has changed. Because we named each subdirectory after part of
the URL where the package is hosted, our new import path looks a lot like that URL:

git
hub.
com/h
eadfi
rst
go/k
eyb
oar
d

We only need to update the import statement in each program. Because the package
name is the same, references to the package in the rest of the code will be unchanged.

With those changes made, all the programs that rely on our keyboard package should
resume working normally.

DOWNLOADING AND INSTALLING PACKAGES WITH


"GO GET"
Using a package's hosting URL as an import path has another benefit. The go tool has
another subcommand named goget that can automatically download and install
packages for you.

We've set up a Git repository with the greeting package that we showed you previously at
this URL:

htt
ps:/
/gith
ub.co
m/h
eadf
irs
tgo
/gre
eting

That means that from any computer with Go installed, you can type this in a terminal:

gogetgi
thu
b.com
/he
adfi
rst
go/
gree
ting

The go tool will connect to github.com, download the Git repository at the
/he
adfi
rstgo
/gree
tin
g path, and save it in your Go workspace's src directory (Note: if the
system doesn't have Git installed, you'll be prompted to install it when you run the go
get command. Just follow the instructions on your screen. The goget command can also
work with Subversion, Mercurial, and Bazaar repositories.)

The goget command will automatically create whatever subdirectories are needed to set
up the appropriate import path. (A github.com directory, a headfirstgo directory, etc.) The
packages saved in the src directory will look like this:

With the packages saved in the Go workspace, they're ready for use in programs. You
can use the greeting, dansk, and deutsch packages in a program with an import statement
like this:

import (
       "github.com/headfirstgo/greeting"
       "github.com/headfirstgo/greeting/dansk"
       "github.com/headfirstgo/greeting/deutsch"
)

The goget command works for other packages, too. This command would install the
key
boar
d package we showed you previously:

gogetgi
thu
b.com
/he
adfi
rst
go/
keyb
oard

In fact, the goget command works for any package that has been set up properly on a
hosting service, no matter who the author is. All you'll need to do is run goget and give
it the package import path. The tool will look at the part of the path that corresponds to
the host address, connect to that host, and download the package at the URL
represented by the rest of the import path. It makes using other developers' code really
easy!

We've set up a Go workspace with a simple package named mypackage. Complete the
program below to import mypackage and call its MyFunction function.

Your code here:
Answers on page 25.

READING PACKAGE
DOCUMENTATION WITH "GO DOC"

You can get a documentation for a package by passing its import path to godoc. For
example, we can get info on the strconv package by running godocstrconv.
The output includes the package name and import path (which are one and the same in
this case), a description of the package as a whole, and a list of all the functions the
package includes.

You can also use godoc to get detailed info on specific functions by providing a function
name following the package name. Suppose we saw the ParseFloat function in the list of
the strconv package's functions and we wanted to know more about it. We could bring
up its documentation with godocstrconvParsefloat.

You'll get back a description of the function and what it does:

The first line looks just like a function declaration would look like in code. It includes
the function name, followed by parentheses containing the names and types of the
parameters it takes (if any). If there are any return values, those will appear after the
parameters.

This is followed by a detailed description of what the function does, along with any
other information developers need in order to use it.

We can get documentation for our keyboard package in the same way, by providing its
import path to godoc. Let's see if there's anything there that will help our would­be user.
From a terminal, run:

godocgithub.com/headfirstgo/keyboard

The godoc tool is able to derive basic information like the package name and import
path from the code. But there's no package description, so it's not that helpful.
Requesting info on the GetFloat function doesn't get us a description either:

DOCUMENTING YOUR PACKAGES WITH DOC


COMMENTS
The godoc tool works hard to add useful info to its output based on examining the code.
Package names and import paths are added for you. So are function names,
parameters, and return values.

But godoc isn't magic. If you want your users to see documentation of a package or
function's intent, you'll need to add it yourself.

Fortunately, that's easy to do: you simply add doc comments to your code. Ordinary
Go comments that appear immediately before a package or function declaration are
treated as doc comments, and will be displayed in godoc's output.

Let's try adding doc comments for the keyboard package. At the top of the keyboard.go file,
immediately before the package line, we'll add a comment describing what the package
does. And immediately before the declaration of GetFloat, we'll add a couple comment
lines describing that function.

The next time we run godoc for the package, it will find the comment before the package
line and convert it to a package description. And when we run godoc for the GetFloat
function, we'll see a description based on the comment lines we added above GetFloat's
declaration.
Being able to display documentation via godoc makes developers that install a package
happy.

And doc comments make developers who work on a package's code happy, too! They're
ordinary comments, so they're easy to add. And you can easily refer to them while
making changes to the code.

There are a few conventions to follow when adding doc comments:

• Comments should be complete sentences.
• Package comments should begin with "Package" followed by the package name.

// Package mypackage enables widget management.

• Function comments should begin with the name of the function they describe.

// MyFunction converts widgets to gizmos.

• You can include code examples in your comments by indenting them.

• Other than indentation for code samples, don't add extra punctuation characters for
emphasis or formatting. Doc comments will be displayed as plain text, and should be
formatted that way.

VIEWING DOCUMENTATION IN A WEB BROWSER


If you're more comfortable in a web browser than a terminal, there are other ways to
view package documentation.

The simplest is to type the word "golang" followed by the name of the package you want
into your favorite search engine. ("Golang" is commonly used for web searches
regarding the Go language because "go" is too common a word to be useful for filtering
out irrelevant results.) If we wanted documentation for the fmt package, we could search
for "golang fmt":

The results should include sites that offer Go documentation in HTML format. If you're
searching for a package in the Go standard library (like fmt), one of the top results will
probably be from golang.org, a site run by the Go development team. The documentation
will have much the same contents as the output of the godoc tool, with package names,
import paths, and descriptions.
One major advantage of the HTML documentation is that each function name in the list
of the package's functions will be a handy clickable link leading to the function
documentation.

But the content is just the same as what you'd see when running godoc in your terminal.
It's all based on the same simple doc comments in the code.

SERVING HTML DOCUMENTATION TO YOURSELF


WITH "GODOC"
The same software that powers the golang.org site's documentation section is actually
available on your computer, too. It's a tool called godoc (not to be confused with the "go
" command), and it's automatically installed along with Go. The godoc tool generates
doc

HTML documentation based on the code in your main Go installation and your
workspace, and includes a web server that can share the resulting pages with browsers.
(Don't worry, with its default settings godoc won't accept connections from any computer
other than your own.)

To run godoc in web server mode, we'll type the godoc command (again, don't confuse
that with "godoc") in a terminal, followed by a special option: -http=:6060.
Then with godoc running, you can type the URL:

http://localhost:6060/pkg

...into your web browser's address bar and press Enter. Your browser will connect to
your own computer, and the godoc server will respond with an HTML page. You'll be
presented with a list of all the packages installed on your machine.

Each package name in the list is a link to that package's documentation. Click it, and
you'll see the same package docs that you'd see on golang.org.

THE "GODOC" SERVER INCLUDES YOUR


PACKAGES!
If we scroll further through our local godoc server's list of packages, we'll see something
interesting: our keyboard package!

In addition to packages from Go's standard library, the godoc tool also builds HTML
documentation for any packages in your Go workspace. These could be third­party
packages you've installed, or packages you've written yourself.

Click the keyboard link, and you'll be taken to the package's documentation. The docs will
include any doc comments from our code!

When you're ready to stop the godoc server, return to your terminal window, then hold
the Ctrl key and press C. You'll be returned to your system prompt.

Go makes it easy to document your packages, which makes packages easier to share,
which makes them easier for other developers to use. It's just one more feature that
makes packages a great way to share code!

YOUR GO TOOLBOX

That's it for Chapter 4! You’ve added packages to your toolbox.
 BULLET POINTS

 By default, the workspace directory is a directory named go within your user's home
directory.

 You can use another directory as your workspace by setting the GOPATH environment
variable.

 Go uses three subdirectories within the workspace: the bin directory holds compiled
executable programs, the pkg directory holds compiled package code, and the src
directory holds Go source code.

 The names of the directories within the src directory are used to form a package's
import path. Names of nested directories are separated by / characters in the import
path.

 The package's name is determined by the package declarations at the top of the
source code files within the package directory. Except for the main package, the
package name should be the same as the name of the directory that contains it.
 Package names should be all lower­case, and ideally consist of a single word.

 A package's functions can only be called from outside that package if they're
exported. A function is exported if its name begins with a capital letter.

 The goinstall command compiles a package's code and stores it in the pkg directory
for general packages, or the bin directory for executable programs.

 A common convention is to use the URL where a package is hosted as its import
path. This allows the goget tool to find, download, and install packages given only
their import path.

 The godoc tool displays documentation for packages. Doc comments within the code
are included in godoc's output.

POOL PUZZLE SOLUTION


Your job is to take code snippets from the pool and place them into the blank lines.
Don't use the same snippet more than once, and you won’t need to use all the snippets.
Your goal is to set up a calc package within a Go workspace so calc's functions can be
used within command.go.
We've set up a Go workspace with a simple package named mypackage. Complete the
program below to import mypackage and call its MyFunction function.
5 arrays
History

Topics

Tutorials On the List


Offers & Deals

Highlights

Settings

Support

Sign Out

A whole lot of programs deal with lists of things. Lists of addresses.

Lists of phone numbers. Lists of products. Go has two ways of storing lists built­in. 
This                            
 
chapter   introduce
will     the
  first:
    arrays.
    You'll
  learn
  about
    to  create  arrays,
how   how to
   data,
fill them with   and how  to  get that
  data
  back
    again.
out    Then
  you'll
  learn
  about
   
processing
    all
  the
  elements
  in array, first the hard way with forloops, and then the
easy
way with for... rangeloops.

ARRAYS
   
HOLD
   
COLLECTIONS
       
OF VALUES
               
A local restaurant owner has a problem. He needs to know how much beef to order for
the upcoming week. If he orders too much, the excess will go to waste. If he doesn't
order enough, he'll have to tell his customers that he can't make their favorite dishes.

He keeps data on how much meat was used the previous three weeks. He needs a
program that will give him some idea how much to order.

This should be simple enough: we can calculate an average by taking the three
amounts, adding them together, and dividing by 3. The average should offer a good
estimate of how much to order.

The first issue is going to be storing the sample values. It would be a pain to declare
three separate variables, and even more so if we wanted to average more values
together later. But, like most programming languages, go offers a data structure that's
perfect for this sort of situation...

An array is a collection of values that all share the same type. Think of it like one of
those pill boxes with compartments — you can store and retrieve pills from each
compartment separately, but it's also easy to transport the container as a whole.

The values an array holds are called its elements. You can have an array of strings, an
array of booleans, or an array of any other Go type (even an array of arrays). You can
store an entire array in a single variable, and then access any element within the array
that you need.
An array holds a specific number of elements, and it cannot grow or shrink. To declare
a variable that holds an array, you need to specify the number of elements it holds in
square brackets ([]), followed by the type of elements the array holds.

To set the array elements' values or to retrieve values later, you'll need a way to specify
which element you mean. Elements in an array are numbered, starting with 0. An
element's number is called its index.

If you wanted to make an array with the names of notes on a musical scale, for example,
the first note would be assigned to index 0. The second note would be at index 1. And so
forth. The index is specified in square brackets.
Here's an array of integers:

And an array of time.Time values:

ZERO VALUES IN ARRAYS


As with variables, when an array is created, all the values it contains are initialized to
the zero value for the type that array holds. So an array of int values is filled with zeros
by default:

The zero value for strings, however, is an empty string, so an array of string values is
filled with empty strings by default:

As long as you know what you're doing, zero values make it safe to manipulate an array
element even if you haven't explicitly assigned a value to it. For example, here we have
an array of integer counters. We can increment any of them without explicitly assigning
a value first, because we know they will all start from 0.
ARRAY LITERALS
If you know in advance what values an array should hold, you can initialize the array
with those values using an array literal. An array literal starts just like an array type,
with the number of elements it will hold in square brackets, followed by the type of its
elements. This is followed by a list in curly braces of the initial values each element
should have. The element values should be separated by commas.

These examples are just like the previous ones we showed, except that instead of
assigning values to the array elements one by one, the entire array is initialized using
array literals.

Using an array literal also allows you to do short variable declarations with :=.
Below is a program that declares a couple arrays and prints out their elements. Write
down what the program output would be.

Answers on page 25.
ACCESSING ARRAY ELEMENTS
WITHIN A LOOP
You don't have to explicitly write the integer index of the array element you're accessing
in your code. You can also use the value in an integer variable as the array index.

That means you can do things like process elements of an array using a for loop. You
loop through indexes in the array, and use the loop variable to access the element at the
current index.

When accessing array elements using a variable, you need to be careful which index
values you use. As we mentioned, arrays hold a specific number of elements. Trying to
access an index that is outside the array will cause a panic, an error that occurs while
your program is running (as opposed to when it's compiling).

Normally, a panic causes your program to crash and an error message to be shown to
the user. Needless to say, panics should be avoided whenever possible.

CHECKING ARRAY LENGTH WITH THE "LEN"


FUNCTION
Writing loops that only access valid array indexes can be somewhat error­prone.
Fortunately, there are a couple ways to make the process easier.

The first is to check the actual number of elements in the array before accessing it. You
can do this with the built­in len function, which returns the length of the array (the
number of elements it contains.

When setting up a loop to process an entire array, you can use len to determine which
indexes are safe to access.
This still has the potential for mistakes, though. If len(notes) returns 7, the highest index
you can access is 6 (because array indexes start at 0, not 1). If you try to access index 7,
you'll get a panic.

LOOPING OVER ARRAYS SAFELY WITH "FOR ...


RANGE"
An even safer way to process each element of an array is to use the special for ... range
loop. In the range form, you provide a variable that will hold the integer index of each
element, another variable that will hold the value of the element itself, and the array
you want to loop over. The loop will run once for each element in the array, assigning
the element's index to your first variable and the element's value to your second
variable. You can add code to the loop block to process those values.

This form of the for loop has no messy init, condition and post expressions. And
because the element value is automatically assigned to a variable for you, there's no risk
that you'll accidentally access an invalid array index. Because it's safer and easier to
read, you'll see the for loop's range form used most often when working with arrays and
other collections.

Here's our previous code that prints each value in our array of musical notes, updated
to use a for ... range loop:

The loop runs seven times, once for each element of the notes array. For each element,
the index variable gets set to the element's index, and the note variable gets set to the
element's value. Then we print the index and value.

USING THE BLANK IDENTIFIER WITH "FOR ...


RANGE" LOOPS
As always, Go requires that you use every variable you declare. If we stop using the index
variable from our for ... range loop, we'll get a compile error:

And the same would be true if we didn't use the variable that holds the element value:

Remember in Chapter 2, when we were calling a function with multiple return values,
and we wanted to ignore one of them? We assigned that value to the blank identifier ( _
), which causes Go to discard that value, without giving a compiler error...

We can do the same with values from for ... range loops. If we don't need the index for
each array element, we can just assign it to the blank identifier:

And if we don't need the value variable, we can assign that to the blank identifier
instead:

GETTING A SUM OF THE NUMBERS IN AN ARRAY

We finally know everything we need to create an array of float64 values and calculate
their average. Let's take the amounts of beef that were used in previous weeks, and
incorporate them into a program, named average.

The first thing we'll need to do is set up a program file. In your Go workspace directory
(which is a go directory within your user's home directory, unless you've set the GOPATH
environment variable), create the following nested directories (if they don't already
exist). Within the innermost directory, average, save a file named command.go.
Now let's write our program code within the command.go file. Since this will be an
executable program, our code will be part of the main package, and our code will reside
in the main function.

We'll start by just calculating a total for the three sample values; we can go back later to
calculate an average. We use an array literal to create an array of three float64 values,
pre­populated with the sample values from prior weeks. We declare a float64 variable
named sum to hold the total, starting with a value of 0.

Then we use a for ... range loop to process each number. We don't need the element
indexes, so we discard them using the _ blank identifier. We add each number to the
value in sum. After we've totaled all the values, we print sum before exiting.

Let's try compiling and running our program. We'll use the goinstall command to
create an executable. We're going to need to provide our executable's import path to go
. If we used this directory structure...
install

...That means the import path for our package will be github.com/headfirstgo/average. So,
from your terminal, type:

goinstallgithub.com/headfirstgo/average

You can do so from within any directory. The go tool will look for a
git
hub.
com/h
eadfi
rst
go/a
ver
ag directory within your workspace's src directory, and
e

compile any .go files it contains. The resulting executable will be named average, and will
be stored in the bin directory within your Go workspace.

Then, you can use the cd command to change to the bin directory within your Go
workspace. Once you're in bin, you can run the executable by typing ./average (or
ave
rage
.e  on Windows).
xe

The program will print the total of the three values from our array, and exit.

GETTING THE AVERAGE OF THE NUMBERS IN AN


ARRAY
We've got our average program printing a total of the array's values, now let's update it to
print an actual average. To do that, we'll divide the total by the array's length.

Passing the array to the len method returns an int value with the array length. But since
the total in the sum variable is a float64 value, we'll need to convert the length to a float64
as well so we can use them together in a math operation. We store the result in the
 variable. Once that's done, all we have to do is divide sum by sampleCount, and
sampleCount

print the result.

Once the code is updated, we can repeat the previous steps to see the new result: run go
 to recompile the code, change to the bin directory, and run the updated average
install
executable. Instead of the sum of the values in the array, we'll see the average.

POOL PUZZLE

Your job is to take code snippets from the pool and place them into the blank lines in
this code. Don't use the same snippet more than once, and you won’t need to use all
the snippets. Your goal is to make a program that will print the index and value of all
the array elements that fall between 10 and 20 (it should match the output shown).

Answers on page 25.
READING A TEXT FILE
That's true, a program where users have to edit and compile the source code themselves
isn't very user­friendly.

Previously, we've used the standard library's os and bufio packages to read data a line at
a time from the keyboard. We can use the same packages to read data a line at a time
from text files. Let's go on a brief detour to learn how to do that.

Then, we'll come back and update the average program to read its numbers in from a text
file.

In your favorite text editor, create a new file named data.txt. Save it somewhere outside
of your Go workspace directory for now.

Within the file, enter our three floating­point sample values, one number per line.
Before we can update our program to average numbers from a text file, we need to be
able to read the file's contents. To start, let's write a program that only reads the file,
and then we'll incorporate what we learn into our averaging program.

In the same directory as data.txt, create a new program named readfile.go. We'll just be
running readfile.go with gorun, rather than installing it, so it's okay to save it outside of
your Go workspace directory. Save the following code in readfile.go. (We'll take a closer
look at how this code works on the next page.)

Then, from your terminal, change to the directory where you saved the two files, and
run gorunreadfile.go. The program will read the contents of data.txt, and print them out.
Our test readfile.go program is successfully reading the lines of the data.txt file and
printing them out. Let's take a closer look at how the program works.

We start by passing a string with the name of the file we want to open to the os.Open
function. Two values are returned from os.Open: a pointer to an os.File value
representing the opened file, and an error value. As we've seen with so many other
functions, if the error value is nil it means the file was opened successfully, but any
other value means there was an error. (This could happen if the file is missing or
unreadable.) If that's the case, we log the error message and exit the program.

Then we pass the os.File value to the bufio.NewScanner function. That will return a
b
ufi
o .S
canne
r value which reads from the file.

The Scan method on bufio.Scanner is designed to be used as part of a for loop. It will read a
single line of text from the file, returning true if it read data successfully and false if it
did not. If Scan is used as the condition on a for loop, the loop will continue running as
long as there is more data to be read. Once the end of the file is reached (or there's an
error), Scan will return false, and the loop will exit.

After calling the Scan method on the bufio.Scanner, calling the Text method returns a string
with the data that was read. For this program, we simply call Println within the loop to
print each line out.

Once the loop exits, we're done with the file. Keeping files open consumes resources
from the operating system, so files should always be closed when a program is done
with them. Calling the Close method on the os.File will accomplish this. Like the Open
function, the Close method returns an error value, which will be nil unless there was a
problem. (Unlike Open, Close returns only a single value, as there is no useful value for it
to return other than the error.)
It's also possible that the bufio.Scanner encountered an error while scanning through the
file. If it did, calling the Err method on the scanner will return that error, which we log
before exiting.

READING A TEXT FILE INTO AN ARRAY


Our readfile.go program worked great — we were able to read the lines from of our
data.tx
t file in as strings, and print them out. Now we need to convert those strings to
numbers and store them in an array. Let's create a package named datafile that will do
this for us.

In your Go workspace directory, create a datafile directory within the headfirstgo
directory. Within the datafile directory, save a file named floats.go. (We name it floats.go
because this file will contain code that reads floating­point numbers from files.)

Within floats.go, save the following code. A lot of this is based on code from our test
 program; we've grayed out the parts where the code is identical. We'll explain
readfile.go

the new code in detail on the next page.
We want to be able to read from files other than data.txt, so we accept the name of the
file we should open as a parameter. We set the function up to return two values, an
array of float64 values and an error value. Like most functions that return an error, the
other return value should only be considered usable if the error value is nil.

Next we declare an array of 3 float64 values that will hold the numbers we read from the
file.

Just like in readfile.go, we open the file for reading. The difference is that instead of a
hard­coded string of "data.txt", we open whatever file name was passed to the function.
If an error is encountered, we need to return an array along with the error value, so we
just return the numbers array (even though nothing has been assigned to it yet).
We need to know which array element to assign each line to, so we create a variable to
track the current index.

The code to set up a bufio.Scanner and loop over the file's lines is identical to the code
from readfile.go. The code within the loop is different, however: we need to call
str
c onv
.Parse
floa
t on the string read from the file to convert it to a float64, and assign the
result to the array. If Parsefloat results in an error, we need to return that. And if the
parsing is successful, we need to increment i so that the next number is assigned to the
next array element.

Our code to close the file and report any errors is identical to readfile.go, except that we
return any errors instead of exiting the program directly. If there are no errors, the end
of the GetFloats function will be reached, and the array of float64 values will be returned
along with a nil error.

UPDATING OUR "AVERAGE" PROGRAM TO READ A


TEXT FILE
We're ready to replace the hard­coded array in our average program with an array read
in from the data.txt file!

Writing our datafile package was the hard part. Here in the main program, we only need
to do three things:

• Update our import declaration to include the datafile package, and remove unused
imports that are no longer used in this file.

• Replace our array of hard­coded numbers with a call to datafile.GetFloats("data.txt").

• Check whether we got an error back from GetFloats, and log it and exit if so.

All the remaining code will be exactly the same.

We can compile the program using the same terminal command as before:

goinstallgithub.com/headfirstgo/average

Since our program imports the datafile package, that will automatically be compiled as
well.

We'll need to move the data.txt file to the bin subdirectory of the Go workspace. That's
because we'll be running the average executable from that directory, and it will look for
 in the same directory. Once you've moved data.txt, change into that bin
data.txt

subdirectory.
When we run the average executable, it will load the values from data.txt into an array,
and use them to calculate an average.

If we change the values in data.txt, the average will change as well.

OUR PROGRAM CAN ONLY PROCESS THREE


VALUES!
But there's a problem — the average program only works if there are three or fewer lines
in data.txt. If there are four or more, average will panic and exit when it's run!

When a Go program panics, it outputs a report with information on the line of code
where the problem occurred. In this case, it looks like the problem is on line 23 of the
 file.
floats.go

If we look at line 23 of floats.go, we'll see that it's the part of the GetFloats function where
numbers from the file get added to the array!
Remember when a mistake in a previous code sample led a program to attempt to
access an eighth element of a seven­element array? That program panicked and exited,
too.

The same problem is happening in our GetFloats function. Because we declared that the
 array holds 3 elements, that's all it can hold. When the fourth line of the data.txt
numbers

file is reached, it attempts to assign to a fourth element of numbers, which results in a
panic.
Go arrays are fixed in size; they can't grow or shrink. But the data.txt file can have as
many lines as the user wants to add. It looks like we can't use an array to store the
numbers from the file, after all!

YOUR GO TOOLBOX

That's it for Chapter 5! You’ve added arrays to your toolbox.

 BULLET POINTS
 To declare an array variable, include the array length in square brackets and the
type of elements it will hold:
v
armya
rra
y[3]i
nt

 To assign or access an element of an array, provide its index in square brackets.
Indexes start at 0, so the first element of myarray is myarray[0].

 As with variables, the default value for all array elements is the zero value for that
element's type.

 You can set element values at the time an array is created using an array literal:
[3]
int{
4,9,6}

 If you store an index in a variable that is not valid for an array, and then try to
access an array element using that variable as an index, you will get a panic — a
runtime error.

 You can get the number of elements in an array with the built­in len function.

 You can conveniently process all the elements of an array using the special for ...
r
ang loop syntax, which loops through each element and assigns its index and value
e

to variables you provide.

 When using a for ... range loop, you can ignore either the index or value for each
element by assigning it to the _ blank identifier.

 The os.Open function opens a file. It returns a pointer to an os.File value representing
that opened file.

 Passing an os.File value to bufio.NewScanner returns a bufio.Scanner value whose Scan
and Text methods can be used to read a line at a time from the file as strings.

Below is a program that declares a couple arrays and prints out their elements. Write
down what the program output would be.
POOL PUZZLE SOLUTION
6 slices
istory

opics

utorials Appending Issue


Offers & Deals

ighlights

ettings

Support

Sign Out

We've learned we can't add more elements to an array. That's a real

problem for our program, because we don't know in advance how many pieces of data
our file contains. But that's where Go slices come in. Slices are a collection type that
can grow to hold additional items — just the thing to fix our current program! We'll also
see how slices can provide an easier way for users to provide data to all your programs,
and how they can help you write functions that are more convenient to call.

SLICES
There actually is a Go data structure that we can add more values to — it's called a
slice. Like arrays, slices are made up of multiple elements, all of the same type. Unlike
arrays, functions are available for slices that allow us to add extra elements onto the
end.

To declare a variable that holds a slice, you type an empty pair of square brackets,
followed by the type of elements the slice will hold.

This is just like the syntax for declaring an array variable, except that you don't specify
the size.

Declaring a slice variable doesn't automatically create a slice. For that, you can call the
built­in make function. You pass make the type of the slice you want to create (which
should be the same as the type of the variable you're going to assign it to), and the
length of slice it should create.

Once the slice is created, you assign and retrieve its elements using the same syntax you
would for an array.

You don't have to declare the variable and create the slice in separate steps; using make
with a short variable declaration will infer the variable's type for you.
The built­in len function works the same way with slices as it does with arrays. Just pass
le
n a slice, and its length will be returned as an integer.

Both for and for ... range loops work just the same with slices as they do with arrays, too:

SLICE LITERALS
Just like with arrays, if you know in advance what values an slice will start with, you can
initialize the slice with those values using a slice literal. A slice literal looks a lot like
an array literal, but where an array literal has the length of the array in square brackets,
a slice literal's square brackets are empty. The empty brackets are then followed by the
type of elements the slice will hold, and a list in curly braces of the initial values each
element will have.

There's no need to call the make function; using a slice literal in your code will create the
slice and pre­populate it.

These examples are just like the previous ones we showed, except that instead of
assigning values to the slice elements one by one, the entire slice is initialized using
slice literals.
POOL PUZZLE

Your job is to take code snippets from the pool and place them into the blank lines in
this code. Don't use the same snippet more than once, and you won’t need to use all
the snippets. Your goal is to make a program that will run and produce the output
shown..

Answers on page 28.
Because slices are built on top of arrays. You can't understand how slices
work without understanding arrays. Here, we'll show you why...

THE SLICE OPERATOR


Every slice is built on top of an underlying array. It's the underlying array that
actually holds the slice's data; the slice is merely a view into some (or all) of the array's
elements.

When you use the make function or a slice literal to create a slice, the underlying array is
created for you automatically (and you can't access it, except through the slice). But you
can also create the array yourself, and then create a slice based on it with the slice
operator.

The slice operator looks similar to the syntax for accessing an individual element or
slice of an array, except that it has two indexes, the index of the array where the slice
should start, and the index of the array that the slice should stop before.
Notice that we emphasize that the second index is the index the slice will stop before.
That is, the slice should include the elements up to, but not including, the second index.
If you use underlyingArray[i:j] as a slice operator, the resulting slice will actually contain
the elements underlyingArray[i] through underlyingArray[j-1].

(We know, it's counterintuitive. But a similar notation has been used in the
Python programming language for over 20 years, and it seems to work OK.)

If you want a slice to include the last element of an underlying array, you actually
specify a second index that's one beyond the end of the array in your slice operator.

Make sure you don't go any farther than that, though, or you'll get an error:
The slice operator has defaults for both the start and stop indexes. If you omit the start
index, a value of 0 (the first element of the array) will be used.

And if you omit the stop index, everything from the start index to the end of the
underlying array will be included in the resulting slice.

UNDERLYING ARRAYS
As we mentioned, a slice doesn't hold any data itself; it's merely a view into the
elements of an underlying array. You can think of a slice as a microscope, focusing on a
particular portion of the contents of a slide (the underlying array).

When you take a slice of an underlying array, you can only "see" the portion of the
array's elements that are visible through the slice.
It's even possible to have multiple slices point to the same underlying array. Each slice
will then be a view into its own subset of the array's elements. The slices can even
overlap!

CHANGE THE UNDERLYING ARRAY, CHANGE THE


SLICE
Now, here's something to be careful about: because a slice is just a view into the
contents of an array, if you change the underlying array, those changes will also be
visible within the slice!

Assigning a new value to a slice element will change the corresponding element in the
underlying array.

If multiple slices point to the same underlying array, a change to the array's elements
will be visible in all the slices.

Because of these potential issues, you may find it's generally better to create slices using
mak or a slice literal, rather than creating an array and using a slice operator on it. With
e

mak and with slice literals, you never have to work with the underlying array.
e

ADD ONTO A SLICE WITH THE "APPEND"


FUNCTION
Go offers a built­in append function that takes a slice, and one or more values you want to
append to the end of that slice. It returns a new, larger slice with all the same elements
as the original slice, plus the new elements added onto the end.

You don't have to keep track of what index you want to assign new values to, or
anything! Just call append with your slice and the value(s) you want added to the end,
and you'll get a new, longer slice back. It's really that easy!

Well, with one caution...

Notice that we're making sure to assign the return value of append back to the same slice
variable we passed to append. This is to avoid some potentially inconsistent behavior in
the slices returned from append.

A slice's underlying array can't grow in size. If there isn't room to add elements to the
array, all its elements will be copied to a new, larger array, and the old array will be
discarded. But since all this happens behind the scenes in the append function, there's no
easy way to tell whether the slice returned from append has the same underlying array as
the slice you passed in, or a different underlying array. If you keep both slices, this can
lead to some unpredictable behavior.

Below, for example, we have four slices, the last three created by calls to append. Here we
are not following the convention of assigning append's return value back to the same
variable. When we assign a value to an element of the s4 slice, we can see the change
reflected in s3, because s4 and s3 happen to share the same underlying array. But the
change is not reflected in s2 or s1, because they have a different underlying array.

So when calling append, it's conventional to just assign the return value back to the same
slice variable you passed to append. You don't need to worry about whether two slices
have the same underlying array if you're only storing one slice!

READING ADDITIONAL FILE LINES USING SLICES


AND "APPEND"
Now that we know about slices and the append function, we can finally fix our average
program! Remember, average was failing as soon as we added a fourth line to the data.txt
file it reads from:
We traced the problem back to our datafile package, which stores the file lines in an
array that can't grow beyond 3 elements:

Most of our work with slices has just centered around understanding them. Now that
we do, updating the GetFloats method to use a slice instead of an array doesn't involve
much effort.

First, we update the method declaration to return a slice of float64 values instead of an
array. Previously, we stored the array in a variable called numbers; we'll just use that same
variable name to hold the slice. We create the slice using an empty slice literal
(although we could have instead called make([]float64,0) if we wanted). After that, much
of the rest of the code in GetFloat can remain the same — the slice is basically a drop­in
replacement for the array.

The big difference is that instead of assigning values read from the file to a specific
array index, we can just call append to extend the slice and add new values. That means
we can get rid of the code to create and update the i variable that tracks the index. We
assign the float64 value returned from ParseFloat to a new temporary variable, just to
hold it while we check for any errors in parsing. Then we pass the numbers slice and the
new value from the file to append, making sure to assign the return value back to the
num  variable.
bers

All the rest of the code in the GetFloats function can remain as it is.

TRYING OUR IMPROVED PROGRAM


The slice returned from the GetFloats function works like a drop­in replacement for an
array in our main average program, too. In fact, we don't have to make any changes to
the main program!

Because we used a := short variable declaration to assign the GetFloats return value to a
variable, the numbers variable automatically switches from an inferred type of [3]float64
(an array) to a type of []float64 (a slice). And because the for ... range loop and the len
functions work the same way with a slice as they do with an array, no changes are
needed to that code, either!
That means we're ready to try the changes out! Ensure the data.txt file is still saved in
your Go workspace's bin subdirectory, and then compile and run the code using the
same commands as before. It will read all the lines of data.txt, and display their average.
Then try updating data.txt to have more lines, or fewer; it will still work regardless!

Below is a program that takes a slice of an array and then appends elements to the
slice. Write down what the program output would be.
Answers on page 28.
COMMAND-LINE ARGUMENTS

There is an alternative — users could pass the values to the program as
command­line arguments.

Just as you can control the behavior of many Go functions by passing them arguments,
you can pass arguments to many programs you run from the terminal or command
prompt. This is known as a program's command­line interface.

You've already seen command­line arguments used in this very book. When we run the
 ("change directory") command, we pass it the name of the directory we want to
cd

change to as an argument. When we run the go command, we often pass it multiple
arguments: the subcommand (run, install, etc.) we want to use, and the name of the file
or package we want the subcommand to work on.
GETTING COMMAND-LINE ARGUMENTS FROM THE
OS.ARGS SLICE
Let's set up a new version of the average program, called average2, that takes the values to
average as command line arguments.

The os package has a package variable, os.Args, that gets set to a slice of strings
representing the command­line arguments the currently­running program was
executed with. We'll start by simply printing the os.Args slice to see what it contains.

Create a new average2 directory alongside the average directory in your workspace, and
save a command.go file within it.

Then, save the following code in command.go. It simply imports the fmt and os packages,
and passes the os.Args slice to fmt.Println.

// average2 calculates the average of several numbers.
package main

import (
       "fmt"
       "os"
)

func main() {
       fmt.Println(os.Args)
}

Let's try it out. From your terminal or command prompt, run this command to compile
and install the program:

goinstallgithub.com/headfirstgo/average2
That will install an executable file named average2 (or average2.exe on Windows) to your
Go workspace's bin subdirectory. Use the cd command to change to bin, and run average2,
but don't hit the Enter key just yet. Following the program name, type a space, and then
one or more arguments, separated by spaces. Then hit Enter. The program will run, and
print the value of os.Args.

Re­run average2 with different arguments, and you should see different output.

THE SLICE OPERATOR CAN BE USED ON OTHER


SLICES
This is working pretty well, but there's one problem: The name of the executable is
being included as the first element of os.Args.

That should be easy to remove, though. Remember how we used the slice operator to
get a slice that included everything but the first element of an array?

The slice operator can be used on slices just like it can on arrays. If we use a slice
operator of [1:] on os.Args, it will give us a new slice that omits the first element (whose
index is 0), and includes the second element (index 1) through the end of the slice.
If we recompile and re­run average2, this time we'll see that the output includes only the
actual command­line arguments.

UPDATING OUR PROGRAM TO USE COMMAND-LINE


ARGUMENTS
Now that we're able to get the command­line arguments as a slice of strings, let's
update the average2 program to convert the arguments to actual numbers, and calculate
their average. We'll mostly be able to re­use the concepts we learned about in our
original average program and the datafile package.

We use the slice operator on os.Args to omit the program name, and assign the resulting
slice to an arguments variable. We set up a sum variable that will hold the total of all the
numbers we're given. Then we use a for ... range loop to process the elements of the
 slice (using the _ blank identifier to ignore the element index). We use
arguments

 to convert the argument string to a float64. If we get an error, we log it
strconv.ParseFloat

and exit, but otherwise we add the current number to sum.

When we've looped through all the arguments, we use len(arguments) to determine how
many data samples we're averaging. We then divide sum by this sample count to get the
average.
With these changes saved, we can recompile and re­run the program. It will take the
numbers you provide as arguments, and average them. Give as few or as many
arguments as you like; it will still work!

VARIADIC FUNCTIONS
Now that we know about slices, we can cover a feature of Go that we haven't talked
about so far. Have you noticed that some function calls can take as few, or as many,
arguments as needed? Look at fmt.Println or append, for example:

Don't try doing this with just any function, though! With all the functions we've defined
so far, there had to be an exact match between the number of parameters in the
function definition and the number of arguments in the function call. Any difference
would result in a compile error.

So how do Println and append do it? They're declared as variadic functions. A variadic
function is one that can be called with a varying number of arguments. To make a
function variadic, use an ellipsis (...) before the type of the last (or only) function
parameter in the function declaration.

The last parameter of a variadic function receives the variadic arguments as a slice,
which the function can then process like any other slice.

Here's a variadic version of the twoInts function, that works just fine with any number of
arguments:

Here's a similar function that works with strings. Notice that if we provide no variadic
arguments, it's not an error; the function just receives an empty slice.
A function can take one or more non­variadic arguments as well. Although a function
caller can omit variadic arguments (resulting in an empty slice), non­variadic
arguments are always required; it's a compile error to omit those. Only the last
parameter in a function definition can be variadic; you can't place it in front of required
parameters.

USING VARIADIC FUNCTIONS


Here's a maximum function that takes any number of float64 arguments and returns the
greatest value out of all of them. The arguments to maximum are stored in a slice in the
num
ber parameter. To start, we set the current maximum value to -Inf, a special value
s

representing negative infinity, obtained by calling math.Inf. (We could start with a
current maximum of 0, but this way maximum will work with negative numbers.) Then we
use for ... range to process each argument in the numbers slice, comparing it to the current
maximum, and setting it as the new maximum if it's greater. Whatever maximum
remains after processing all the arguments is the one we return.
Here's an inRange function that takes a minimum value, a minimum value, and any
number of additional float64 arguments. It will discard any argument that is below the
given minimum or above the given maximum, returning a slice containing only the
arguments that were in the specified range.

CODE MAGNETS
A Go program that defines and uses a variadic function is scrambled up on the fridge.
Can you reconstruct the code snippets to make a working program that will produce the
given output?

Answers on page 29.
USING A VARIADIC FUNCTION TO
CALCULATE AVERAGES
Let's create a variadic average function that can take any number of float64 arguments
and return their average. It will look much like the logic from our average2 program.
We'll set up a sum variable to hold the total of the argument values. Then we'll loop
through the range of arguments, adding each one to the value in sum. Finally, we'll divide
 by the number of arguments (converted to a float64) to get the average. The result is
sum

a function that can average as many (or as few) numbers as we want.
PASSING SLICES TO VARIADIC FUNCTIONS
Our new average variadic function works so well, we should try updating our average2
program to make use of it. We can paste the average function into our average2 code as­is.

In the main function, we're still going to need to convert each of the command­line
arguments from a string to a float64 value. We'll create a slice to hold the resulting
values, and store it in a variable named numbers. After each command­line argument is
converted, instead of using it to calculate the average directly, we'll just append it to the
nu
mber slice.
s

We then attempt to pass the numbers slice to the average function. But when we go to
compile the program, that results in an error...
The average function is expecting one or more float64 arguments, not a slice of float64
values...

So what now? Are we forced to choose between making our functions variadic and
being able to pass slices to them?

Fortunately, Go provides special syntax for this situation. When calling a variadic
function, simply add an ellipsis (...) following the slice you want to use in place of
variadic arguments.
So all we need to do is add an ellipsis following the numbers slice in our call to average...

With that change made, we should be able to compile and run our program again. It
will convert our command­line arguments to a slice of float64 values, then pass that
slice to the variadic average function.

SLICES HAVE SAVED THE DAY!


Working with lists of values is essential for any programming language. With arrays
and slices, you can keep your data in collections of whatever size you need. And with
features like for ... range loops, Go makes it easy to process the data in those collections,
too!

YOUR GO TOOLBOX

That's it for Chapter 6! You’ve added slices to your toolbox.
 BULLET POINTS

 A slice variable is declared just like an array variable, except the length is omitted:
varmyslice[]int

 For the most part, code for working with slices is identical to code that works with
arrays. This includes: accessing elements, using zero values, passing slices to the len
function, and for ... range loops.

 A slice literal looks just like an array literal, except the length is omitted: []int{1,7,
10}

 You can get a slice that contains elements i through j-1 of an array or slice using
the slice operator: s[i:j]

 The os.Args package variable contains a slice of strings with the command­line
arguments the current program was run with.
 A variadic function is one that can be called with a varying number of arguments.

 To declare a variadic function, place an ellipsis (...) before the type of the last
parameter in the function declaration. That parameter will then receive all the
variadic arguments as a slice.

 When calling a variadic function, you can use a slice in place of the variadic
arguments by typing an ellipsis after the slice:
inR
ange
(1,1
0,myslice...)

POOL PUZZLE SOLUTION

Below is a program that takes a slice of an array and then appends elements to the
slice. Write down what the program output would be.
CODE MAGNETS SOLUTION
7 maps
History

Topics

Tutorials Labeling Data


Offers & Deals

Highlights

Settings

Support

Sign Out

Throwing things in piles is fine, until you need to find

something again. You've already seen how to create lists of values using arrays

and slices. You've seen how to apply the same operation to every value in an array or
slice. But what if you need to work with a particular value? To find it, you'll have to
start at the beginning of the array or hash, and look through Every. Single. Value.

What if there were a kind of collection where every value had a label on it? You could
quickly find just the value you needed! In this chapter, we'll look at maps, which do
just that.
COUNTING VOTES
A seat on the Sleepy Creek County School Board is up for grabs this year, and polls have
been showing that the election is really close. Now that it’s election night, the
candidates are excitedly watching the votes roll in.

This is another example that debuted in Head First Ruby, in the hashes
chapter. Ruby hashes are a lot like Go maps, so this example works great here,
too!

There are two candidates on the ballot, Amber Graham and Brian Martin. Voters also
have the option to "write in" a candidate's name (that is, type in a name that doesn't
appear on the ballot). Those won't be as common as the main candidates, but we can
expect a few such names to appear.

The electronic voting machines in use this year record the votes to text files, one vote
per line. (Budgets are tight, so the city council chose the cheap voting machine vendor.)

Here’s a file with all the votes for District A:

We need to process each line of the file and tally the total number of times each name
occurs. The name with the most votes will be our winner!

READING NAMES FROM A FILE


Our first order of business is to read the contents of the votes.txt file. The datafile
package from previous chapters already has a GetFloats function that reads each line of a
file into a slice, but GetFloats can only read float64 values. We're going to need a separate
function that can return the file lines as a slice of string values.

So let's start by creating a strings.go file alongside the floats.go file in the datafile
package directory. In that file, we'll add a GetStrings function. The code in GetStrings will
look much like the code in GetFloats (we've grayed out the code that's identical below).
But instead of converting each line to a float64 value, GetStrings will just add the line
directly to the slice we're returning, as a string value.

Now let's create the program that will actually count the votes. We'll name it count.
Within your Go workspace, go into the src/github.com/headfirstgo directory and create a
new directory named count. Then create a file named command.go within the count directory.

Before writing the full program, let's confirm that our GetStrings function is working. At
the top of the main function, we'll call datafile.GetStrings, passing it "votes.txt" as the name
of the file to read from. We'll store the returned slice of strings in a new variable named
lin
es, and any error in a variable named err. As usual, if err is not nil, we'll log the error
and exit. Otherwise, we'll simply call fmt.Println to print out the contents of the strings
slice.

As we've done with other programs, you can compile this program (plus any packages it
depends on, datafile in this case) by running goinstall and providing it the package
import path. If you used the directory structure shown above, that import path should
be github.com/headfirstgo/count.

That will save an executable file named count (or count.exe on Windows) in the bin
subdirectory of your Go workspace.

As with the data.txt file in previous chapters, we need to ensure a votes.txt file is saved
in the current directory when we run our program. In the bin subdirectory of your Go
workspace, save a file with the contents shown at right. In your terminal, use the cd
command to change to that same subdirectory.
Now you should be able to run the executable by typing ./count (or count.exe on
Windows). It should read every line of votes.txt into a slice of strings, then print that
slice out.

COUNTING NAMES THE HARD WAY, WITH SLICES


Reading a slice of names from the file didn't require learning anything new. But now
comes the challenge: how do we count the number of times each name occurs? We'll
show you two ways, first with slices, and then with a new data structure, maps.

For our first solution, we'll create two slices, each with the same number of elements, in
a specific order. The first slice would hold the names we found in the file, with each
name occurring once. We could call that one names. The second slice, counts, would hold
the number of times each name was found in the file. The element counts[0] would hold
the count for names[0], counts[1] would hold the count for names[1], and so on.

Let's update the count program to actually count the number of times each name occurs
in the file. We'll try this plan of using a names slice to hold each unique candidate name,
and a corresponding counts slice to track the number of times each name occurs.
As always, we can re­compile the program with goinstall. If we run the resulting
executable, it will read the votes.txt file and print each name it finds, along with the
number of times that name occurs!

Let's take a closer look at how this works...

Our count program uses a loop nested inside another loop to tally the name counts. The
outer loop assigns lines of the file to the line variable, one at a time. for_,line:=range
lines{

The inner loop searches each element of the names hash, looking for a name equal to the
current line from the file.
Say someone adds a write­in candidate to their ballot, causing a line from the text file to
be loaded with the string "CarlosDiaz". The program will check the elements of names, one
by one, to see if any of them equal "CarlosDiaz".

If none matches, the program will append the string "CarlosDiaz" to the names slice, and a
corresponding count of 1 to the counts slice (because this line represents the first vote for
"CarlosDiaz).
"

But suppose the next line is the string "BrianMartin"... Because that string already exists
in the names slice, the program will find it and add 1 to the corresponding value in counts,
instead.

MAPS
But here's the problem with storing the names in slices: for each and every line of the
file, you have to search through many (if not all) of the values in the names slice to
compare them. That may work okay in a small district like Sleepy Creek County, but in
a bigger district with lots of votes, this approach will be way too slow! "Mikey Moose"?
Nope...
Putting data in a slice is like stacking it in a big pile; you can get particular items back
out, but you'll have to search through everything to find them.

Go has another way of storing collections of data: maps. A map is a collection where is
value is accessed via a key. Keys are an easy way to get data back out of your map. It's
like having neatly­labeled file folders instead of a messy pile.
Whereas arrays and slices can only use integers as indexes, a map can use any type for
keys (as long as values of that type can be compared using ==). That includes numbers,
strings, and more. The values all have to all be of the same type, and the keys all have to
be of the same type, but the keys don't have to be the same type as the values.

To declare a variable that holds a map, you type the map keyword, followed by square
brackets ([]) containing the key type. Then, following the brackets, provide the value
type.

Just as with slices, declaring a map variable doesn't automatically create a map; you
need to call the make function (the same function you can use to create slices). Instead of
a slice type, you can pass make the type of the map you want to create (which should be
the same as the type of the variable you're going to assign it to).

var ranks map[string]int
ranks = make(map[string]int)

You may find it's easier to just use a short variable declaration, though:

ranks := make(map[string]int)
The syntax to assign values to a map and get them back out again looks a lot like the
syntax to assign and get values for arrays or slices. But while arrays and slices only let
you use integers as element indexes, maps let you use any value of the type you chose
for keys.

Here's another map with strings as keys and strings as values:

Here's a map with integers as keys and booleans as values:

MAP LITERALS
Just as with arrays and slices, if you know keys and values that you want your map to
have in advance, you can use a map literal to create it. A map literal starts with the
map type (in the form map[KeyType]ValueType). This is followed by curly braces containing
key/value pairs you want the map to start with. For each key/value pair, you include the
key, a colon, and then the value. Multiple key/value pairs are separated by commas.

Here are a couple of the preceding map examples, re­created using map literals:

As with slice literals, leaving the curly braces empty creates a map that starts empty.
You may find this preferable to using make to create maps.

Fill in the blanks in the below program, so it will produce the output shown.

Answers on page 23.

ZERO VALUES IN MAPS


As with arrays and slices, if you access a map key that hasn't been assigned to, you'll get
a zero value back.

Depending on the value type, the zero value may not actually be 0. For maps with a
value type of string, for example, the zero value will be an empty string.
As with arrays and slices, zero values can make it safe to manipulate a map value even if
you haven't explicitly assigned to it yet.

HOW TO TELL ZERO VALUES APART FROM


ASSIGNED VALUES
Zero values, although useful, can sometimes make it difficult to tell whether a given key
has been assigned the zero value, or if it has never been assigned.

Here's an example of a program where this could be an issue. This code erroneously
reports that the student "Carl" is failing, when in reality he just hasn't had any grades
logged:

To address situations like this, accessing a map key optionally returns a second,
boolean value. It will be true if the returned value has actually been assigned to the map,
or false if the returned value just represents the default zero value. Most Go developers
assign this boolean value to a variable named ok (because the name is nice and short).

If you only want to test whether a value is present, the value itself can be ignored by
assigning it to the _ blank identifier.
The second return value can be used to decide whether you should treat the value you
got from the map as an assigned value that just happens to match the zero value for
that type, or as an unassigned value.

Here's an update to our code that tests whether the requested key has actually had a
value assigned before it reports a failing grade:

Write down what the output of the below program snippet would be.

REMOVING KEY/VALUE PAIRS WITH THE "DELETE"


FUNCTION
At some point after assigning a value to a key, you may want to remove it from your
map. Go provides the built­in delete function for this purpose. Just pass delete the map
you want to delete a key from, and the key you want deleted. That key and its
corresponding value will be removed from the map.

In the code below, we assign values to keys in two different hashes, then delete them
again. After that, when we try accessing those keys, we get a zero value (which is 0 for
the ranks map, false for the isPrime map). The secondary boolean value is also false in
each case, which means that the key is not present.

UPDATING OUR VOTE COUNTING PROGRAM TO


USE MAPS
Now that we understand maps a bit better, let's see if we can use what we've learned to
simplify our vote counting program.

Previously, we used a pair of slices, one called names that held candidate names, and one
called counts held vote counts for each name. For each name we read from the file, we
had to search through the slice of names, one by one, for a match. We then incremented
the vote count for that name in the corresponding element of the counts slice.

Using a map will be much simpler. We can replace the two slices with a single map
(which we'll also call counts). Our map will use candidate names as its keys, and integers
(which will hold the vote counts for that name) as its values. Once that's set up, all we
have to do is use each candidate name we read from the file as a map key, and
increment the value that key holds.

Here's some simplified code that creates a map and increments the values for some
candidate names directly:

Our previous program needed separate logic to add new elements to both slices if the
name wasn't found...

But we don't need to do that with a map. If the key we're accessing doesn't already exist,
we'll get the zero value back (literally 0 in this case, since our values are integers). We
then increment that value, giving us 1, which gets assigned to the map. When we
encounter that name again, we'll get the assigned value, which we can then increment
as normal.

Next, let's try incorporating our counts map into the actual program, so it can tally the
votes from the actual file.
We'll be honest; after all that work to learn about maps, the final code looks a little anti­
climactic! We replace the two slice declarations with a single map declaration. Next is
the code in the loop that processes strings from the file. We replace the original eleven
lines of code there with a single line, which increments the count in the map for the
current candidate name. And we replace the loop at the end that prints the results with
a single line that prints the whole counts map.

Trust us, though, the code only looks anti­climactic. There are still complex operations
going on here. But the map is handling them all for you, which means you don't have to
write as much code!

As before, you can re­compile the program using the goinstall command. When we re­
run the executable, the votes.txt file will be loaded and processed. We'll see the counts
map printed, with the number of times each name was encountered in the file.

USING FOR ... RANGE LOOPS WITH MAPS


That's true. A format of one name and one vote count per line would probably be better:

To format each key and value from the map as a separate line, we're going to need to
loop through each entry in the map.

The same for ... range loop we've been using to process array and slice elements works on
maps, too. Instead of assigning an integer index to the first variable you provide,
however, the current map key will be assigned.

The for ... range loop makes it easy to loop through a map's keys and values. Just provide
a variable to hold each key, and another to hold the corresponding value, and it will
automatically loop through each entry in the map.
If you only need to loop through the keys, you can omit the variable that holds the
values:

And if you only need the values, you can assign the keys to the _ blank identifier:

But there's one potential issue with this example... If you save the preceding example to
a file and run it with gorun, you'll find that the map keys and values are printed in a
random order. If you run the program multiple times, you'll get a different order each
time.

THE FOR ... RANGE LOOP HANDLES MAPS IN


RANDOM ORDER!
The for ... range loop processes map keys and values in a random order because a map is
an unordered collection of keys and values. When you use for ... range loop with a map,
you never know what order you'll get the map's contents in! Sometimes that's fine, but
if you need more consistent ordering, you'll need to write the code for that yourself.
Here's an update to the previous program that always prints the names in alphabetical
order. It does using two separate for loops. The first loops over each key in the map,
ignoring the values, and adds them to a slice of strings. Then, the slice is passed to the
sor
t package's Strings method to sort it alphabetically, in­place.

The second for loop doesn't loop over the map, it loops over the sorted slice of names.
(Which, thanks to the preceding code, now contains every key from the map in
alphabetical order.) It prints the name, and then gets the value that matches that name
from the map. It still processes every key and value in the map, but it gets the keys from
the sorted slice, not the map itself.

If we save the above code and run it, this time the student names are printed in
alphabetical order. This will be true no matter how many times we run the program.

If it doesn't matter what order your map data is processed in, using a for ... range loop
directly on the map will probably work for you. But if order matters, you may want to
consider setting up your own code to handle the processing order.

UPDATING OUR VOTE COUNTING PROGRAM WITH


A FOR ... RANGE LOOP
There aren't a lot of candidates in Sleepy Creek County, so we don't see a need to sort
the output by name. We'll just use a for ... range loop to process the keys and values
directly from the map.

It's a pretty simple change to make; we just replace the line that prints the entire map
with a for ... range loop. We'll assign each key to a name variable, and each value to a count
variable. Then we'll call Printf to print the current candidate name and vote count.

Another compilation via goinstall, another run of the executable, and we'll see our
output in its new format. Each candidate name and their vote count is here, neatly
formatted on its own line.

When the only data collections we had available were arrays and slices, we needed a lot
of extra code and processing time to look values up. But maps have made the process
easy! Anytime you need to be able to find a collection's values again, you should
consider using a map!
CODE MAGNETS

A Go program that uses a for ... range loop to print out the contents of a map is
scrambled up on the fridge. Can you reconstruct the code snippets to make a working
program that will produce the given output? (It's okay if the output order differs
between runs of the program.)

YOUR GO TOOLBOX

That's it for Chapter 7! You’ve added maps to your toolbox.
 BULLET POINTS

 When declaring a map variable, you must provide the types for its keys and its
values: varmymapmap[string]int

 To assign a value to a map, provide the key you want to assign it to in square
brackets: mymap["mykey"]=12

 To get a value, you provide the key as well: fmt.Println(mymap["mykey"])

 You can create a map and initialize it with data at the same time using a map literal:
map[string]int{"a":2,"b":3}

 As with arrays and slices, if you access a map key that hasn't been assigned a value,
you'll get a zero value back.

 Getting a value from a map can return a second, optional boolean value that
indicates whether that value was assigned, or if it represents a default zero value:
value,ok:=mymap["c"]

 If you only want to test whether a key has had a value assigned, you can ignore the
actual value using the _ blank identifier:
_,ok:=mymap["c"]
 You can delete keys and their corresponding values from a map using the delete
built­in function:
delete(
myma
p,"
b")

 You can use for ... range loops with maps, much like you can with arrays or slices.
You provide one variable that will be assigned each key in turn, and a second variable
that will be assigned each value in turn.
forkey
,val
ue:=rangem
ymap{

   
 fmt
.Prin
tln(key,val
ue)

 The for ... range loop processes map key/value pairs in random order. If you need a
specific order, you'll need to handle that yourself.

Fill in the blanks in the below program, so it will produce the output shown.

Write down what the output of the below program snippet would be.
CODE MAGNETS SOLUTION
8 structs
story

opics

utorials Building Storage


ffers & Deals

ghlights

ettings

Support

Sign Out

Sometimes you need to store more than one type of data. We

learned about slices, which store a list of values. Then we learned about maps, which
map a list of keys to a list of values. But both of these data structures can only hold
values of one type. Sometimes, you need to group together values of several types.
Think of mailing addresses, where you have to mix street names (strings) with postal
codes (integers). Or student records, where you have to mix student names (strings)
with grade point averages (floating­point numbers). You can't mix value types in slices
or maps. But you can if you use another type called a struct. We'll learn all about
structs in this chapter!
SLICES AND MAPS HOLD VALUES OF ONE TYPE
Gopher Fancy is a new magazine devoted to lovable rodents. They're currently working
on a system to keep track of their subscriber base.

STRUCTS ARE BUILT OUT OF VALUES OF MANY


TYPES
A struct (short for "structure") is a value that is constructed out of other values of
many different types. Whereas a slice might only be able to hold string values or a map
might only be able to hold int values, you can create a struct that holds string values, int
values, float64 values, bool values, and more — all in one convenient grouping.
You declare a struct type using the struct keyword, followed by curly braces. Within the
braces, you can define one or more fields: values that the struct groups together. Each
field definition appears on a separate line, and consists of a field name, followed by the
type of value that field will hold.

You can use a struct type as the type of a variable you're declaring. This code declares a
variable named myStruct that holds structs that have a float64 field named number, a string
field named word, and a bool field named toggle:

(It's more common to use a defined type to declare struct variables, but we
won't cover type definitions for a few more pages, so we'll write it this way for
now.)
When we call Printf with the %#v verb above, it prints the value in myStruct as a struct
literal. We'll be covering struct literals later in the chapter, but for now you can see that
the struct's number field has been set to 0, the word field to an empty string, and the toggle
field to false. Each field has been set to the zero value for its type.

ACCESS STRUCT FIELDS USING THE DOT


OPERATOR
Now we can define a struct, but to actually use it, we need a way to store new values in
the struct's fields and retrieve them again.

All along, we've been using the dot operator to indicate functions that "belong to"
another package, or methods that "belong to" a value:

Similarly, we can use a dot operator to indicate fields that "belong to" a struct. This
works both for assigning values, and retrieving them.

We can use dot operators to assign values to all the fields of myStruct, and then print
them back out:
Don't worry about the number of spaces between struct field names
and their types.

When you write your struct fields, just insert a single space between the field
name and its type. When you run the gofmt command on your files (which
you should always do) it will insert extra spaces so that all the types align
vertically. The alignment just makes the code easier to read; it doesn't
change its meaning at all!

STORING SUBSCRIBER DATA IN A STRUCT


Now that we know how to declare a variable that holds a struct and assign values to its
fields, we can create a struct to hold magazine subscriber data.

First, we'll define a variable named subscriber. We'll give subscriber a struct type with name
(string), rate (float64), and active (bool) fields.

With the variable and its type declared, we can then use dot operators to access the
struct's fields. We assign values of the appropriate type to each field, and then print the
values back out again.
Even though the data we have for a subscriber is stored using a variety of types, structs
let us keep it all in one convenient package!

At the right is a program that creates a struct variable which holds a pet's name (a
) and age (an int). Fill in the blanks so that the code will produce the output
string

shown.

DEFINED TYPES AND STRUCTS


Throughout this book, you've used a variety of types, like int, string, bool, slices, maps,
and now structs. But you haven't been able to create completely new types.

Type definitions allow you to create types of your own. They let you create a new
defined type that's based on an underlying type.

Although you can use any type as an underlying type, such as float64, string, or even
slices or maps, in this chapter we're going to focus on using struct types as underlying
types. We'll try using other underlying types when we take a deeper look at defined
types in the next chapter.

To write a type definition, use the type keyword, followed by the name for your new
defined type, and then the underlying type you want to base it on. If you're using a
struct type as your underlying type, you'll use the struct keyword followed by a list of
field definitions in curly braces, just as you did when declaring struct variables.

Just like variables, type definitions can be written within a function. But that will limit
its scope to that function's block, meaning you won't be able to use it outside that
function. So types are usually defined outside of any functions, at the package level.
As a quick demonstration, the below code defines two types: part, and car. Each defined
type uses a struct as its underlying type.

Then, within the main function, we declare a porsche variable of the car type, and a bolts
variable of the part type. There's no need to re­write the lengthy struct definitions when
declaring the variables; we just use the names of the defined types.

With the variables declared, we can set the values of their struct fields and get the
values back out, just as we did in previous programs.

USING A DEFINED TYPE FOR MAGAZINE


SUBSCRIBERS
Previously, to create more than one variable that stored magazine subscriber data in a
struct, we had to write out the full struct type (including all its fields) for each variable.
But now, we can simply define a subscriber type at the package level. We write the struct
type just once, as the underlying type for the defined type. When we're ready to declare
variables, we don't have to write the struct type again; we simply use subscriber as their
type. No more need to repeat the entire struct definition!

USING DEFINED TYPES WITH FUNCTIONS


Defined types can be used for more than just variable types. They also work for function
parameters and return values.

Here's our part type again, together with a new printInfo function that prints a part's
fields. The function takes a single parameter, with part as its type. Within printInfo, we
access the fields via the parameter variable just like any other struct variable's.
And here's a minimumOrder function that creates a part with a specified description and a
predefined value for the count field. We declare minimumOrder's return type to be part so it
can return the new struct.

Here are a couple functions that work with the magazine's subscriber type...

The printInfo function takes a subscriber as a parameter, and prints the values of its
fields.

We also have a defaultSubscriber function that sets up a new subscriber struct with some
default values. It takes a string parameter called name, and uses that to set a new
s
ubs
c ri
be value's name field. Then it sets the rate and active fields to default values.
r

Finally, it returns the completed subscriber struct to its caller.
In our main function, we can pass subscriber names to defaultSubscriber to get a new
s
ubs
c r
ibe structs. One subscriber gets a discounted rate, so we re­set that struct field
r

directly. We can pass filled­out subscriber structs to printInfo to print out their contents.

CODE MAGNETS

A Go program is scrambled up on the fridge. Can you reconstruct the code snippets to
make a working program that will produce the given output? The finished program will
have a defined struct type named student, and a printInfo function that accepts a student
value as a parameter.

Don't use an existing type name as a variable name!

If you've defined a type named car in the current package, and you declare a
variable that's also named car, the variable name will shadow (take
precedence over) the type name, making it inaccessible.

This isn't a common problem in practice, because defined types are often
exported from their packages (and their names are therefore capitalized),
and variables often are not (and their names are therefore lower­case). Car
(an exported type name) can't conflict with car (an unexported variable
name). We'll see more about exporting defined types later in the chapter.
Still, shadowing is a confusing problem when it occurs, so it's good to be
aware that it can happen.
MODIFYING A STRUCT USING A FUNCTION

Our friends at Gopher Fancy are trying to write a function that takes a struct as a
parameter, and updates one of the fields in that struct.

Remember way back in Chapter 3, when we were trying to write a double function that
took a number and doubled it? After double returned, the number was back to its
original value!

That's when we learned that Go is a "pass­by­value" language, meaning that function
parameters receive a copy of the arguments the function was called with. If a function
changes a parameter value, it's changing the copy, not the original.

The same thing is true for structs. When we pass a subscriber struct to applyDiscount, the
function receives a copy of the struct. So when we set the rate field on the struct, we're
modifying the copied struct, not the original.
Back in Chapter 3, our solution was to update the function parameter to accept a
pointer to a value, instead of accepting a value directly. When calling the function, we
used the address­of operator (&) to pass a pointer to the value we wanted to update.
Then, within the function, we used the * operator to update the value at that pointer.

As a result, the updated value was still visible after the function returned.

We can use pointers to allow a function to update a struct, as well.

Here's an updated version of the applyDiscount function that should work correctly. We
update the s parameter to accept a pointer to a subscriber struct, rather than the struct
itself. Then we update the value in the struct's rate field.

In main, we call applyDiscount with a pointer to a subscriber struct. When we print the value
in the struct's rate field, we can see that it's been updated successfully!
ACCESSING STRUCT FIELDS THROUGH A POINTER
If you try to print a pointer variable, what you'll see is the memory address it points to.
This is generally not what you want.

Instead, you need to use the * operator (what we like to call the "value­at operator") to
get the value at the pointer.

So you might think you'd need to use the * operator with pointers to structs as well. But
just putting a * before the struct pointer won't work:

If you write *pointer.myField, Go thinks that myField must contain a pointer. It doesn't,
though, so an error results. To get this to work, you need to wrap *pointer in
parentheses. That will cause the myStruct value to be retrieved, after which you can
access the struct field.

Having to write (*pointer).myField all the time would get tedious quickly, though. For this
reason, the dot operator lets you access fields via pointers to structs, just as you can
access fields directly from struct values. You can leave off the parentheses and the *
operator.

This works for assigning to struct fields through a pointer as well:

And that's how the applyDiscount function is able to update the struct field without using
the * operator. It assigns to the rate field through the struct pointer.
THERE ARE NO DUMB QUESTIONS
Q: You showed a defaultSubscriber function before that set a struct's fields, but
it didn't need to use any pointers! Why not?

A: The defaultSubscriber function returned a struct value. If a caller stores the returned
value, then the values in its fields will be preserved. Only functions that modify existing
structs without returning them have to use pointers for those changes to be preserved.

But defaultSubscriber could have returned a pointer to a struct, if we had wanted it to. In
fact, we make just that change in the next section!

PASS LARGE STRUCTS USING POINTERS


Functions receive a copy of the arguments they're called with, even if they're a big value
like a struct.

That's why, unless your struct has only a couple small fields, it's often a good idea to
pass functions a pointer to a struct, rather than the struct itself. (This is true even if the
function doesn't need to modify the struct.) When you pass a struct pointer, only one
copy of the original struct exists in memory. The function just receives the memory
address of that single struct, and can read the struct, modify it, or whatever else it needs
to do, all without making an extra copy.
Here's our defaultSubscriber function, updated to return a pointer, and our printInfo
function, updated to receive a pointer. Neither of these functions needs to change an
existing struct like applyDiscount does. But using pointers ensures that only one copy of
each struct needs to be kept in memory, while still allowing the program to work as
normal.

The two programs below aren't working quite right. The nitroBoost function in the
left­hand program is supposed to add 50 kilometers/hour to a car's top speed, but it's
not. And the doublePack function in the right­hand program is supposed to double a
 value's count field, but it's not, either.
part

See if you can fix the programs. Only minimal changes will be necessary; we've left a
little extra space in the code so you can make the necessary updates.

MOVING OUR STRUCT TYPE TO A DIFFERENT


PACKAGE
That should be easy to do. Find the headfirstgo directory within your Go workspace, and
create a new directory in there to hold a package named magazine. Within magazine, create
a file named magazine.go.

Be sure to add a packagemagazine declaration at the top of magazine.go. Then, copy the
 struct definition from your existing code, and paste it into magazine.go.
subscriber

Next, let's create a program to try out the new package. Since we're just experimenting
for now, let's not create a separate package folder for this code; we'll just run it using
the gorun command. Create a file named main.go. You can save it in any directory you
want, but make sure you save it outside your Go workspace, so it doesn't interfere with
any other packages.

Within main.go, save this code, which simply creates a new subscriber struct and accesses
one of its fields.

There are two differences from the previous examples. First, we need to import the
 package at the top of the file. Second, we need to use magazine.subscriber as the
magazine

type name, since it belongs to another package now.
A DEFINED TYPE'S NAME MUST BE CAPITALIZED
TO BE EXPORTED
Let's see if our experimental code can still access the subscriber struct type in its new
package. In your terminal, change into the directory where you saved main.go, then enter
.
gorunmain.go

We get a couple errors, but here's the important one: cannotrefertounexportedname
mag
a zin
e.sub
scribe.
r

Go type names follow the same rule as variable and function names: if the name of
variable, function, or type begins with a capital letter, it is considered exported and can
be accessed from outside the package it's declared in. But our subscriber type name
begins with a lower­case letter. That means it can only be used within the magazine
package.

Well, that seems like an easy fix. We'll just open our magazine.go file and capitalize the
name of the defined type. Then, we'll open main.go and capitalize the names of any
references to that type. (There's just one right now.)

For a type to be accessed outside the package it's defined in, it must be
exported: its name must begin with a capital letter.
If we try running the updated code with gorunmain.go, we no longer get the error saying
that the magazine.subscriber type is unexported. So that seems to be fixed. But we get a
couple new errors in its place...

STRUCT FIELD NAMES MUST BE CAPITALIZED TO


BE EXPORTED
With the Subscriber type name capitalized, we seem to be able to access it from the main
package. But now we're getting an error saying that we can't refer to the rate field,
because that is unexported.

Even if a struct type is exported from a package, its fields will be unexported if their
names don't begin with a capital letter. Let's try capitalizing Rate (in both magazine.go and
main.g)...
o

Struct field names must also be capitalized if you want to export them from
their package.
Run main.go again, and you'll see that everything works this time. Now that they're
exported, we can access the Subscriber type and its Rate field from the main package.

Notice that the code worked even though the name and active fields were still unexported.
You can have a mixture of exported and unexported fields within a single struct type, if
you want.

That's probably not advisable in the case of the Subscriber type, though. It wouldn't make
sense to be able to access the subscription rate from other packages, but not the name
or address. So let's go back into magazine.go and export the other fields as well. Simply
capitalize their names: Name and Active.

STRUCT LITERALS
The code to define a struct and then assign values to its fields one by one can get a bit
tedious:

var subscriber magazine.Subscriber
subscriber.Name = "Aman Singh"
subscriber.Rate = 4.99
subscriber.Active = true
So, just as with slices and maps, Go offers struct literals to let you create a struct and
set its fields at the same time.

The syntax looks similar to a map literal. The type is listed first, followed by curly
braces. Within the braces, you can specify values for some or all of the struct fields,
using the field name, a colon, and then the value. If you specify multiple fields, separate
them with commas.

Above, we showed some code that creates a Subscriber struct and sets its fields, one by
one. This code does the same thing in a single line, using a struct literal:

You may have noticed that for most of the chapter, we've had to use long­form
declarations for struct variables (unless the struct was being returned from a function).
Struct literals allow us to use short variable declarations for a struct we've just created.

You can omit some or even all of the fields from the curly braces. Omitted fields will be
set to the zero value for their type.

POOL PUZZLE

Your job is to take code snippets from the pool and place them into the blank lines in
this code. Don't use the same snippet more than once, and you won’t need to use all
the snippets. Your goal is to make a program that will run and produce the output
shown.

Answers on page 22.
CREATING AN EMPLOYEE STRUCT
TYPE

Adding an Employee struct type should be pretty easy. We'll just add it to the magazine
package, alongside the Subscriber type. In magazine.go, define a new Employee type, with a
str  underlying type. Give the struct type a Name field with a type of string, and a Salary
uct

field with a type of float64. Be sure to capitalize the type name and all the fields, so that
they're exported from the magazine package.

We can update the main function in main.go to try the new type out. First, declare a
variable with the type magazine.Employee. Then assign values of the appropriate type to
each of the fields. Finally, print those values out.

If you execute gorunmain.go from your terminal, it should run, create a new
 struct, set its field values, and then print those values out.
magazine.Employee

CREATING AN ADDRESS STRUCT TYPE


Next, we need to track mailing addresses for both the Subscriber and Employee types. We're
going to need fields for the street address, city, state, and postal code (zip code).

We could add separate fields to both the Subscriber and Employee types, like this:

...But mailing addresses are going to have the same format, no matter what type they
belong to. It's a pain to have to repeat all those fields between multiple types.
Struct fields can hold values of any type, including other structs. So instead, let's try
building an Address struct type, and then adding an Address field on the Subscriber and
Emp
loye
e types. That will save us some effort now, and ensure consistency between the
types later if we have to change the address format.

We'll create just the Address type first, so we can ensure it's working correctly. Place it in
the magazine package, alongside the Subscriber and Employee types. Then, replace the code
in main.go with a few lines to create an Address and ensure its fields are accessible.

Type gorunmain.go in your terminal, and it should create an Address struct, populate its
fields, and then print the whole struct out.

ADDING A STRUCT AS A FIELD ON ANOTHER TYPE


Now that we're sure the Address struct type works by itself, let's add HomeAddress fields to
the Subscriber and Employee types.

Adding a struct field that is itself a struct type is no different than adding a field of any
other type. You provide a name for the field, followed by the field's type (which in this
case will be a struct type).

Add a field named HomeAddress to the Subscriber struct. Make sure to capitalize the field
name, so that it's accessible from outside the magazine package. Then specify the field
type, which is Address.

Add a HomeAddress field to the Employee type as well.
SETTING UP A STRUCT WITHIN ANOTHER STRUCT
Now let's see if we can populate the fields of the Address struct within the Subscriber
struct. There are a couple ways to go about this.

The first approach is to create an entirely separate Address struct, and then use it to set
the entire Address field of the Subscriber struct. Here's an update to main.go that follows
this approach.

Type gorunmain.go in your terminal, and you'll see the subscriber's HomeAddress field has
been set to the struct you built.

Another approach is to set the fields of the inner struct through the outer struct.

When a Subscriber struct is created, its HomeAddress field is already set: it's an Address struct
with all its fields set to their zero values. If we print HomeAddress using the "%#v" verb for
fmt
.Pr
in , it will print the struct as it would appear in Go code, that is, as a struct
tf

literal. We'll see that each of the Address fields is set to an empty string, which is the zero
value for the string type.

If subscriber is a variable that contains a Subscriber struct, then when you type
, you'll get an Address struct, even if you haven't explicitly set
subscriber.HomeAddress

.
HomeAddress

You can use this fact to "chain" dot operators together so you can access the fields of the
 struct. Simply type subscriber.HomeAddress to access the Address struct, followed by
Address

another dot operator and the name of the field you want to access on that Address struct.

This works both for assigning values to the inner struct's fields...

subscriber.HomeAddress.PostalCode = "68111"

...And for retrieving those values again later.

fmt.Println("Postal Code:", subscriber.HomeAddress.PostalCode)

Here's an update to main.go that uses dot operator chaining. First we store a Subscriber
struct in the subscriber variable. That will automatically create an Address struct in
's HomeAddress field. We set values for subscriber.HomeAddress.Street,
subscriber

, etc., and then print those values out again.
subscriber.HomeAddress.City

Then we store an Employee struct in the employee variable, and do the same for its
 struct.
HomeAddress
Type gorunmain.go in your terminal, and the program will print out the completed fields
of both subscriber.HomeAddress and employee.HomeAddress.

ANONYMOUS STRUCT FIELDS


The code to access the fields of an inner struct through its outer struct can be a bit
tedious, though. You have to write the field name of the inner struct (HomeAddress) each
time you want to access any of the fields it contains.

Go allows you to define anonymous fields: struct fields that have no name of their
own, just a type. We can use an anonymous field to make our inner struct easier to
access.
Here's an update to the Subscriber and Employee types to convert their HomeAddress fields to
an anonymous field. To do this, we simply remove the field name, leaving only the type.

When you declare an anonymous field, you can use the field's type name as if it were
the name of the field. So subscriber.Address and employee.Address in the code below still
access the Address structs:

EMBEDDING STRUCTS
But anonymous fields offer much more than just the ability to skip providing a name
for a field in a struct definition.

An inner struct that is stored within an outer struct using an anonymous field is said to
be embedded within the outer struct. You can access fields for an embedded struct as
if they belonged to the outer struct.

So now that the Address struct type is embedded within the Subscriber and Employee struct
types, you don't have to write out subscriber.Address.City to get at the City field; you can
just write subscriber.City. You don't need to write employee.Address.State; you can just
write employee.State.

Here's one last version of main.go, updated to treat Address as an embedded type. You can
write the code as if there was no Address type at all; it's like the Address fields belong to the
struct type they're embedded within.

You can access fields for an embedded struct as if they belong to the outer
struct.

OUR DEFINED TYPES ARE COMPLETE!


Nice work! You've defined Subscriber and Employee struct types, and embedded an Address
struct in each of them. You've found a way to represent all the data the magazine
needed!

You're still missing an important aspect to defined types, though. In previous chapters,
you've used types like time.Time and strings.Replacer that have methods: functions that
you can call on their values. But you haven't learned how to define methods for your
own types yet. Don't worry; we'll learn all about it in the next chapter!

Here's a source file from the geo package, which we saw in a previous exercise. Your
goal is to make the code in main.go work correctly. But here's the catch: you need to do
it by adding just two fields to the Landmark struct type within geo.go.
YOUR GO TOOLBOX

That's it for Chapter 7! You’ve added maps to your toolbox.
 BULLET POINTS

 You can declare a variable with a struct type. To specify a struct type, use the struct
keyword, followed by a list of field names and types within curly braces.
varmyStructstruct{

   field1string

   field2int

 Writing struct types repeatedly can get tedious, so it's usually best to define a type
with an underlying struct type. Then the defined type can be used for variables,
function parameters or return values, etc.
t
y p
emy
Types
t ruct{
 
  fiel
d1st
ring

v
armyV
army
Type

 Struct fields are accessed via the dot operator.
m
yVar.f
iel
d1="
val
ue"

f
mt.Pri
ntl
n(m
yVa
r.f
ield
1)

 If a function needs to modify a struct or if a struct is large, it should be passed to the
function as a pointer.

 Types will only be exported from the package they're defined in if their name begins
with a capital letter.

 Likewise, struct fields will not be accessible outside their package unless their name
is capitalized.

 Struct literals let you create a struct and set its fields at the same time.
myV
ar:
=myT
ype{
fie
ld1:"
valu
e"}

 Adding a struct field with no name, only a type, defines an anonymous field.

 An inner struct that is added as part of an outer struct using an anonymous field is
said to be embedded within the outer struct.

 You can access the fields of an embedded struct as if they belong to the outer struct.

At the right is a program that creates a struct variable which holds a pet's name (a
strin) and age (an int). Fill in the blanks so that the code will produce the output
g

shown.
CODE MAGNETS SOLUTION

The two programs below weren't working quite right. The nitroBoost function in the
left­hand program is supposed to add 50 kilometers/hour to a car's top speed, but it
wasn't. And the doublePack function in the right­hand program is supposed to double a
 value's count field, but it wasn't, either.
part
Fixing both programs was simply a matter of updating the functions to accept
pointers, and updating the function calls to pass pointers. The code within the
functions that updates the struct fields doesn't need to be changed; the code to access
a field through a pointer to a struct is the same as the code to access a field on the
struct directly.

POOL PUZZLE SOLUTION


The geo.go source file is from the geo package, which we saw in a previous exercise.
Your goal was to make the code in main.go work correctly, by adding just two fields to
the Landmark struct type within geo.go.
9 defined types
History

Topics

Tutorials You're My Type


Offers & Deals

Highlights

Settings

Support

Sign Out

There's more to learn about defined types. In the previous chapter, we

showed you how to define a type with a struct underlying type. What we didn't show
you was that you can use any type as an underlying type.
And do you remember methods — the special kind of function that's associated with
values of a particular type? We've been calling methods on various values throughout
the book, but we haven't shown you how to define your own methods.
In this chapter, we're going to fix all of that. Let's get started!

TYPE ERRORS IN REAL LIFE


If you live in the U.S.A., you are probably used to the quirky system of measurement
used here. At gas stations, for example, fuel is sold by the "gallon", a volume nearly four
times the size of the "liter" used in much of the rest of the world.

Steve is an American, renting a car in another country. He pulls into a gas station to
refuel. He intends to purchase 10 gallons, figuring that will be enough to reach his hotel
in another city.

He gets back on the road, but only gets one­fourth of the way to his destination before
running out of fuel.

If Steve had looked at the labels on the gas pump more closely, he would have realized
that it was measuring the fuel in liters, not gallons, and that he needed to purchase
37.85 liters to get the equivalent of 10 gallons.
When you have a number, it's best to be certain what that number is measuring. You
want to know if it's liters or gallons, kilograms or pounds, dollars or yen.

DEFINED TYPES WITH UNDERLYING BASIC TYPES


If you have the following variable:

var fuel float64 = 10

...Does that represent 10 gallons or 10 liters? The person who wrote that declaration
knows, but no one else does, not for sure.

You can use Go's defined types to make it clear what a value is to be used for. Although
defined types most commonly use structs as their underlying types, they can be based
on int, float64, string, bool, or any other type.

Go defined types most often use structs as their underlying types, but they
can also be based on ints, strings, booleans, or any other type.

Here's a program that defines two new types, Liters and Gallons, both with an underlying
type of float64. These are defined at package level, so that they're available within any
function in the current package.

Within the main function, we declare a variable with a type of Gallons, and another with a
type of Liters. We assign values to each variable, and then print them out.

Once you've defined a type, you can do a conversion to that type from any value of the
underlying type. As with any other conversion, you write the type you want to convert
to, followed by the value you want to convert in parentheses.

If we had wanted, we could have written short variable declarations in the code above
using type conversions:

carFuel := Gallons(10.0)
busFuel := Liters(240.0)

You can assign a value of the underlying type to a variable using the defined type; the
conversion will be performed automatically.

If you have a variable that uses a defined type, you cannot assign a value of a different
defined type to it, even if the other type has the same underlying type. This helps
protect developers from confusing the two types.

You can convert between types that have the same underlying type. So Liters can be
converted to Gallons and vice­versa, because both have an underlying type of float64. But
Go only considers the value of the underlying type when doing a conversion; there is no
difference between Gallons(Liters(240.0)) and Gallons(240.0). Simply converting raw values
from one type to another defeats the protection against conversion errors that types are
supposed to provide.

Instead, you'll want to perform whatever operations are necessary to convert the
underlying type value to a value appropriate for the type you're converting to.

A quick web search shows that one liter equals roughly 0.264 gallons, and that one
gallon equals roughly 3.785 liters. We can multiply by these conversion rates to convert
from Gallons to Liters, and vice versa.
DEFINED TYPES AND OPERATORS
A defined type supports all the same operations as its underlying type. Types based on
f
loa
t 6, for example, support arithmetic operators like +, -, *, and /, as well as
4

comparison operators like ==, >, and <.

A type based on an underlying type of string, however, would support +, ==, >, and <, but
not -, because - is not a valid operator for strings.

A defined type can be used in operations together with values of its underlying type:

But defined types cannot be used in operations together with values of a different type,
even if the other type has the same underlying type. Again, this is to protect developers
from accidentally mixing the two types.

fmt.Println(Liters(1.2) + Gallons(3.4))
fmt.Println(Gallons(1.2) == Liters(1.2))

If you want to add a value in Liters to a value in Gallons, you'll need to convert one type
to match the other first.
POOL PUZZLE

Your job is to take code snippets from the pool and place them into the blank lines in
this code. Don't use the same snippet more than once, and you won’t need to use all
the snippets. Your goal is to make a program that will run and produce the output
shown.

CONVERTING BETWEEN TYPES USING FUNCTIONS


Suppose we wanted to take a car whose fuel level is measured in Gallons and refill it at a
gas pump that measures in Liters. Or take a bus whose fuel is measured in Liters and
refill it at a gas pump that measures in Gallons. To protect us from inaccurate
measurements, Go will give us a compile error if we try to combine values of different
types:

In order to do operations with values of different types, we need to convert the types to
match first. Previously, we demonstrated multiplying a Liters value by 0.264 and
converted the result to Gallons. We also multiplied a Gallons value by 3.785 and converted
the result to Liters.

We can create ToGallons and ToLiters functions that do the same thing, then call them to
perform the conversion for us:
Gasoline isn't the only liquid we need to measure the volume of. There's cooking oil,
bottles of soda, juice, etc. And so there are many more measures of volume than just
liters and gallons. In the U.S.A. there are teaspoons, cups, quarts, and more. The metric
system has other units of measure as well, but the milliliter (1/1000th of a liter) is the
most commonly used.

Let's add a new type, Milliliters. Like the others, it will use float64 as an underlying type.

We're also going to want a way to convert from Milliliters to the other types. But if we
start adding a function to convert from Milliliters to Gallons, we run into a problem: we
can't have two ToGallons functions in the same package!

We could rename the two ToGallons functions to include the type they're converting
from: LitersToGallons and MillilitersToGallons, respectively. But those names would be a
pain to write out all the time, and as we start adding functions to convert between the
other types, it becomes clear this isn't sustainable.

THERE ARE NO DUMB QUESTIONS


Q: I've seen other languages that support function overloading: they allow
you to have multiple functions with the same name, as long as their
parameter types are different. Doesn't Go support that?

A: The Go maintainers get this question frequently too, and they answer it at
https://golang.org/doc/faq#overloading: "Experience with other languages told us that
having a variety of methods with the same name but different signatures was
occasionally useful but that it could also be confusing and fragile in practice." The Go
language is simplified by not supporting overloading, and so it doesn't support it. As
you'll see later in the book, the Go team made similar decisions in other areas of the
language, too; when they have to choose between simplicity and adding more features,
they generally choose simplicity. But that's okay! As we'll see shortly, there are other
ways to get the same benefits...

FIXING OUR FUNCTION NAME CONFLICT USING


METHODS
Remember way back in Chapter 2, we introduced you to methods, which are functions
associated with values of a given type? Among other things, we created a time.Time value
and called its Year method, and we created a strings.Replacer value and called its Replace
method.

We can define methods of our own to help with our type conversion problem.

We're not allowed to have multiple functions named ToGallons, so we had to write long,
cumbersome function names that incorporated the type we were converting:

LitersToGallons(Liters(2))
MillilitersToGallons(Milliliters(500))

But we can have multiple methods named ToGallons, as long as they're defined on
separate types. Not having to worry about name conflicts will let us make our method
names much shorter.

Liters(2).ToGallons()
Milliliters(500).ToGallons()

But let's not get ahead of ourselves. Before we can do anything else, we need to know
how to define a method...

DEFINING METHODS
A method definition is very similar to a function definition. In fact, there's really only
one difference: you add one extra parameter, a receiver parameter, in parentheses
before the function name.
As with any function parameter, you need to provide a name for the receiver parameter,
followed by a type.

To call a method you've defined, you write the value you're calling the method on, a dot,
and the name of the method you're calling followed by parentheses. The value you're
calling the method on is known as the method receiver.

The similarity between method calls and method definitions can help you remember
the syntax: the receiver is listed first when calling a method, and the receiver parameter
is listed first when defining a method.

The name of the receiver parameter in the method definition isn't important, but its
type is: the method you're defining becomes associated with all values of that type.

Below, we define a type named MyType, with an underlying type of string. Then, we define
a method named sayHi. Because sayHi has a receiver parameter with a type of MyType, we'll
be able to call the sayHi method on any MyType value. (Most developers would say that
sayH is defined "on" MyType.)
i
Once a method is defined on a type, it can be called on any value of that type.

Here, we create two different MyType values, and call sayHi on each of them.

THE RECEIVER PARAMETER IS (PRETTY MUCH)


JUST ANOTHER PARAMETER
The type of the receiver parameter is the type that the method becomes associated with.
But aside from that, the receiver parameter doesn't get special treatment from Go. You
can access its contents within the method block just like you would any other function
parameter.

The code sample below is almost identical to the previous one, except that we've
updated it to print the value of the receiver parameter. You can see the receivers in the
resulting output.

Go lets you name a receiver parameter whatever you want, but it's more readable if all
the methods you define for a type have receiver parameters with the same name.

By convention, Go developers usually use a name consisting of a single letter — the first
letter of the receiver's type name, in lower case. (This is why we used m as the name for
our MyType receiver parameter.)

The receiver parameter is Go's equivalent to "self" or "this" values in other
languages.

THERE ARE NO DUMB QUESTIONS


Q: Can I define new methods on any type?

A: Only types that are defined in the same package where you define the method. That
means no defining methods for types from someone else's security package from your
hac  package, and no defining new methods on universal types like int or string.
king

Q: But I need to be able to use methods of my own with someone else's
type!

A: First you should consider whether a function would work well enough; a function
can take any type you want as a parameter. But if you really need a value that has some
methods of your own, plus some methods from a type in another package, you can
make a struct type that embeds the other package's type as an anonymous field. We'll
look at how that works in the next chapter.

Q: I've seen other languages where a method receiver was available in a
method block in a special variable named self or this. Does Go do that?

A: The receiver parameter is Go's equivalent to self/this. You can use receiver
parameters in the same way, and there's no need for Go to reserve self or this as
keywords! (You could even name your receiver parameter this if you wanted, but don't
do that; the convention is to use the first letter of the receiver's type name instead.)

A METHOD IS (PRETTY MUCH) JUST LIKE A


FUNCTION
Aside from the fact that they're called on a receiver, methods are otherwise pretty
similar to any other function.

As with any other function, you can define addition parameters within parentheses
following the method name. These parameter variables can be accessed in the method
block, along with the receiver parameter. When you call the method, you'll need to
provide an argument for each parameter.
As with any other function, you can declare one or more return values for a method,
which will be returned when the method is called:

As with any other function, a method is considered exported from the current package
if its name begins with a capital letter, and it's considered unexported if its name begins
with a lower­case letter. If you want to use your method outside the current package, be
sure its name begins with a capital letter.

POINTER RECEIVER PARAMETERS


Here's an issue that may look familiar by now. We've defined a new Number type with an
underlying type of int. We've given Number a double method that is supposed to multiply
the underlying value of its receiver by two, and then update the receiver. But we can see
from the output that the method receiver isn't actually getting updated.

Back in Chapter 3, we had a double function with a similar problem. Back then, we
learned that function parameters receive a copy of the values the function is called with,
not the original values, and that any updates to the copy would be lost when the
function exited. To make the double function work, we had to pass a pointer to the value
we wanted to update, and then update the value at that pointer within the function.

We've said that receiver parameters are treated no differently than ordinary
parameters. And like any other parameter, a receiver parameter receives a copy of the
receiver value. If you make changes to the receiver within a method, you're changing
the copy, not the original.

As with the double function in Chapter 3, the solution is to update our Double method to
use a pointer for its receiver parameter. This is done in the same way as any other
parameter: we place a * in front of the receiver type to indicate it's a pointer type. We'll
also need to modify the method block so that it updates the value at the pointer. Once
that's done, when we call Double on a Number value, the Number should be updated.

Notice that we didn't have to change the method call at all. When you call a method that
requires a pointer receiver on a variable with a non­pointer type, Go will automatically
convert the receiver to a pointer for you. The same is true for variables with pointer
types; if you call a method requiring a value receiver, Go will automatically get the value
at the pointer for you and pass that to the method.
You can see this at work in the code at right. The method named method takes a value
receiver, but we can call it using both direct values and pointers, because Go auto­
converts if needed. And the method named pointerMethod takes a pointer receiver, but we
can call it on both direct values and pointers, because Go will auto­convert if needed.

By the way, the code at right breaks a convention: for consistency, all of your type's
methods can take value receivers, or they can all take pointer receivers, but you should
avoid mixing the two. We're only mixing the two kinds here for demonstration
purposes.

To call a method that requires a pointer receiver, you have to be able
to get a pointer to the value!

You can only get pointers to values that are stored in variables. If you try to
get the address of a value that's not stored in a variable, you'll get an error:

The same limitation applies when calling methods with pointer receivers. Go
can automatically convert values to pointers for you, but only if the receiver
value is stored in a variable. If you try to call a method on the value itself, Go
won't be able to get a pointer, and you'll get a similar error:

Instead, you'll need to store the value in a variable, which will then allow Go
to get a pointer to it:

BREAKING STUFF IS EDUCATIONAL!

Here is our Number type again, with definitions for a couple methods. Make one of the
changes below, and try to compile the code. Then undo your change, and try the next
one. See what happens!

package main

import "fmt"

type Number int

func (n *Number) Display() {
       fmt.Println(*n)
}
func (n *Number) Double() {
       *n *= 2
}

func main() {
       number := Number(4)
       number.Double()
       number.Display()
}

If you do this... ...the code will break because...

Change a receiver You can only define new methods on types that were

parameter to a type declared in the current package. Defining a method on a

not defined in this globally-defined type like i


ntwill result in a compile error.

package:

func(
n*Nu
mber
int)

Double
(){

      
 *n*
=2

Change the receiver Receiver parameters receive a copy of the value the

parameter for Doubl


eto method was called on. If the Doubl
efunction only modifies

a non-pointer type: the copy, the original value will be unchanged when Dou
ble

func(
n*N
umber) exits.

D
oubl
e(){

 
    
  n*
* =2

Call a method that When calling a method that takes a pointer receiver, Go

requires a pointer can automatically convert a value to a pointer to a receiver

receiver on a value if it's stored in a variable. If it's not, you'll get an error.

that's not in a variable:

Number
(4).D
ouble()

Change the receiver The code will actually still work after making this change,

parameter for Displ


ay but it breaks convention! Receiver parameters in the

to a non-pointer type: methods for a type can be all pointers, or all values, but it's

func(
n*Num
ber) best to avoid mixing the two.

D
ispla
y(){

 
     
 fmt.
Pri
ntln(
*n)

}
CONVERTING LITERS AND MILLILITERS TO
GALLONS USING METHODS
When we added a Milliliters type to our defined types for measuring volume, we
discovered we couldn't have ToGallons functions for both Liters and Milliliters. To work
around this, we had to create functions with lengthy names:

func LitersToGallons(l Liters) Gallons {
       return Gallons(l * 0.264)
}
func MillilitersToGallons(m Milliliters) Gallons {
       return Gallons(m * 0.000264)
}

But unlike functions, method names don't have to be unique, as long as they're defined
on different types.

Let's try implementing a ToGallons method on the Liters type. The code will be almost
identical to the LitersToGallons function, but we'll make the Liters value a receiver
parameter rather than an ordinary parameter. Then we'll do the same for the Milliliters
type, converting the MillilitersToGallons function to a ToGallons method.

Notice that we're not using pointer types for the receiver parameters. We're not
modifying the receivers, and the values don't consume much memory, so it's fine for the
parameter to receive a copy of the value.
In our main function, we create a Liters value, then call ToGallons on it. Because the
receiver has the type Liters, the ToGallons method for the Liters type is called. Likewise,
calling ToGallons on a Milliliters value causes the ToGallons method for the Milliliters type
to be called.

CONVERTING GALLONS TO LITERS AND


MILLILITERS USING METHODS
The process is similar when converting the GallonsToLiters and GallonsToMilliliters
functions to methods. We just move the Gallons parameter to a receiver parameter in
each.
The below code should add a ToMilliliters method on the Liters type, and a ToLiters
method on the Milliliters type. The code in the main function should produce the
output shown. Fill in the blanks to complete the code.

YOUR GO TOOLBOX

That's it for Chapter 9! You’ve added method definitions to your toolbox.
 BULLET POINTS

 Once you've defined a type, you can do a conversion to that type from any value of
the underlying type.
Gallons(10.0)

 Once a variable's type is defined, values of other types cannot be assigned to that
variable, even if they have the same underlying type.

 A defined type supports all the same operators as its underlying type. A type based
on int, for example, would support +, -, *, /, ==, >, and < operators.

 A defined type can be used in operations together with values of its underlying type:
Gallons(10.)+2.3

 To define a method, provide a receiver parameter in parentheses before the method
name:
f
unc(mMy
Type)M
yMet
hod
(){

 The receiver parameter can be used within the method block like any other
parameter:
f
unc(mMy
Typ
e)My
Met
hod
(){

 
  fmt.
Pri
ntl
n("
cal
ledo
n",m
)

 You can define additional parameters or return values on a method, just as you
would with any other function.

 Defining multiple functions with the same name in the same package is not
allowed, even if they have parameters of different types. But you can define multiple
methods with the same name, as long as each is defined on a different type.

 You can only define methods on types that were defined in the same package.

 As with any other parameter, receiver parameters receive a copy of the original
value. If your method needs to modify the receiver, you should use a pointer type for
the receiver parameter, and modify the value at that pointer.

POOL PUZZLE SOLUTION


The below code should add a ToMilliliters method on the Liters type, and a ToLiters
method on the Milliliters type. The code in the main function should produce the
output shown. Fill in the blanks to complete the code.
10
ory
encapsulation and embedding
cs

orials Keep it to Yourself


rs & Deals

hlights

ings

Support

Sign Out

Mistakes happen. Your struct type's methods work great, if the struct's fields are

set correctly. But suppose you're using data from a file to set those struct fields. Are you
sure all that data is valid? If it's not, users could get strange errors when your type's
methods are called! In this chapter, you'll learn about encapsulation: a way to protect
those fields from invalid data.
And what if your struct type needs methods that already exist on another type? You
don't have to copy and paste the method code. If you embed the other type within your
struct type, its methods will be promoted to your struct type. You can use the methods
just as if they were defined on your own type! This chapter will show you how that
works, too.
CREATING A DATE STRUCT TYPE
A local startup called Remind Me is developing a calendar application to help users
remember birthdays, anniversaries, and more.

The year, month, and day sound like they all need to be grouped together; none of those
values would be useful by itself. A struct type would probably be useful for keeping
those separate values together in a single bundle.

As we've seen, defined types can use any other type as their underlying type, including
structs. In fact, struct types served as our introduction to defined types, back in chapter
8.

Let's create a Date struct type to hold our year, month, and day values. We'll add Year,
, and Day fields to the struct, each with a type of int. In our main function, we'll run a
Month

quick test of the new type, using a struct literal to create a Date value with all its fields
populated. We'll just use Println to print the Date out for now.
If we run the finished program, we'll see the Year, Month, and Day fields of our Date struct.
It looks like everything's working!

PEOPLE ARE SETTING THE DATE STRUCT FIELD TO


INVALID VALUES!

Ah, we can see how that might happen. Only year numbers 1 or greater are valid, but we
don't have anything preventing users from accidentally setting the Year field to 0 or -999.
Only month numbers from 1 through 12 are valid, but nothing prevents users from
setting the Month field to 0 or 13. Only the numbers 1 through 31 are valid for the Day field,
but users can enter days like -2 or 50.

What we need is a way for our programs to ensure the user data is valid before
accepting it. In computer science, this is known as "data validation". We need to test
that the Year is being set to a value of 1 or greater, the Month is being set between 1 and 12,
and the Day is being set between 1 and 31.

(Yes, some months have fewer than 31 days, but to keep our code samples a
reasonable length, we'll just check that it's between 1 and 31.)
SETTER METHODS
A struct type is just another defined type, and that means you can define methods on it
just like any other. We should be able to create SetYear, SetMonth, and SetDay methods on
the Date type that take a value, check whether it's valid, and if so, set the appropriate
struct field.

This kind of method is often called a setter method. By convention, Go setter
methods are usually named in the form SetX, where X is the thing that you're setting.

Here's a prototype of the SetYear method. The receiver parameter is a pointer to the Date
struct you're calling the method on. SetYear accepts the year you want to set as a
parameter, and sets the Year field on the receiver Date struct. It doesn't validate the value
at all currently, but we'll add validation in a little bit.

In our main method, we create a Date, and call SetYear on it. Then we print the struct's Year
field.

Setter methods are methods used to set fields or other values within a
defined type's underlying value.

When we run the program, though, we'll see that it didn't work quite right. Even though
we create a Date and call SetYear with a new value, the Year field is still set to its zero
value!

SETTER METHODS NEED POINTER RECEIVERS


Remember the Double method on the Number type we showed you earlier? Originally, we
wrote it with a plain value receiver type, Number. But we learned that, like any other
parameter, receiver parameters receive a copy of the original value. The Double method
was updating the copy, which was lost when the function exited.

We needed to update Double to take a pointer receiver type, *Number. When we updated
the value at the pointer, the changes were preserved after Double exited.

The same holds true for SetYear. The Date receiver gets a copy of the original struct. Any
updates to the fields of the copy are lost when SetYear exits!

We can fix SetYear by updating it to take a pointer receiver: (d*Date). That's the only
change that's necessary. We don't have to update the SetYear method block, because
 automatically gets the value at the pointer for us (as if we'd typed (*d).Year). The
d.Year

call to date.SetYear in main doesn't need to be changed either, because the Date value is
automatically converted to a *Date when it's passed to the method.
Now that SetYear takes a pointer receiver, if we re­run the code, we'll see that the Year
field has been updated.

ADDING THE REMAINING SETTER METHODS


Now it should be easy to follow the same pattern to define SetMonth and SetDay methods
on the Date type. We just need to be sure to use a pointer receiver in the method
definition. Go will convert the receiver to a pointer when we call each method, and
convert the pointer back to a struct value when updating its fields.
In main, we can create a Date struct value, set its Year, Month, and Day fields via our new
methods, and print the whole struct out to see the results.

Now we have setter methods for each of our Date type's fields. But even if they use the
methods, users can still accidentally set the fields to invalid values. We'll look at
preventing that next.

In the Chapter 8 exercises, you saw code for a Coordinates struct type. We've moved
that type definition to a coordinates.go file within the geo package directory.
We need to add setter methods to the Coordinates type for each of its fields. Fill in the
blanks in the coordinates.go file below, so that the code in main.go will run and produce
the output shown.
ADDING VALIDATION TO THE SETTER METHODS
Adding validation to our setter methods will take a bit of work, but we learned
everything we need to do it in Chapter 3.

In each setter method, we'll test whether the value is in a valid range. If it's invalid, we'll
return an error value. If it's valid, we'll set the Date struct field as normal, and return nil
for the error value.

Let's add validation to the SetYear method first. We add a declaration that the method
will return a value, of type error. At the start of the method block, we test whether the
y
ea parameter provided by the caller is any number less than 1. If it is, we return an
r

e
rro with a message of "invalidyear". If not, we set the struct's Year field, and return nil,
r

indicating there was no error.
In main, we call SetYear and store its return value in a variable named err. If err is not nil,
it means the assigned value was invalid, so we log the error and exit. Otherwise, we
proceed to print the Date struct's Year field.

Passing an invalid value to SetYear causes the program to report the error and exit. But if
we pass a valid value, the program will proceed to print it out. Looks like our SetYear
method is working!

Validation code in the SetMonth and SetDay methods will be similar to the code in SetYear.

In SetMonth, we test whether the provided month number is less than 1 or greater than 12,
and return an error if so. Otherwise, we set the field and return nil.

And in SetDay, we test whether the provided day of the month is less than 1 or greater
than 31. Invalid values result in a returned error, but valid values cause the field to be
set and nil to be returned.
You can test the setter methods by inserting the code snippets below into the block for
mai...
n

Passing 14 to SetMonth results in an error:

Passing 50 to SetDay results in an error:

But passing 5 to SetMonth works:
But passing 27 to SetDay works:

THE FIELDS CAN STILL BE SET TO INVALID


VALUES!

It's true; there's nothing preventing anyone from setting the Date struct fields directly.
And if they do so, it bypasses the validation code in the setter methods. They can set
any value they want!

We need a way to protect these fields, so that users of our Date type can only update the
fields using the setter methods.

Go provides a way of doing this: we can move the Date type to another package, and
make its date fields unexported.

So far, unexported variables, functions, etc. have mostly gotten in our way. The most
recent example of this was in Chapter 8, when we discovered that even though our
Subscri
b e struct type was exported from the magazine package, its fields were unexported,
r

making them inaccessible outside the magazine package.
But in this case, we don't want the fields to be accessible. Unexported struct fields are
exactly what we need!

Let's try moving our Date type to another package and making its fields unexported, and
see if that fixes our problem.

MOVING THE DATE TYPE TO ANOTHER PACKAGE


In the headfirstgo directory within your Go workspace, create a new directory to hold a
package named calendar. Within calendar, create a file named date.go. (Remember, you
can name the files within a package directory anything you want; they'll all become part
of the same package.)

Within date.go, add a packagecalendar declaration, and import the "errors" package.
(That's the only package that the code in this file will be using.) Then, copy all your old
code for the Date type and paste it into this file.
Next, create a file named main.go. You can save it in any directory you want, but save it
outside your Go workspace, so it doesn't interfere with any other packages.

At this point, code we add in main.go will still be able to create an invalid Date, either by
setting its fields directly, or by using a struct literal.

If we run main.go from the terminal, we'll see that both ways of setting the fields worked,
and two invalid dates are printed.
MAKING DATE FIELDS UNEXPORTED
Now let's try updating the Date struct so that its fields are unexported. That simply
consists of changing the field names to begin with lower­case letters, in the type
definition and everywhere else they occur.

The Date type itself needs to remain exported, as do all of the setter methods, as we will
need to access these from outside the calendar package.

To test our changes, update the field names in main.go to match the field names in
da
te.g.
o
ACCESSING UNEXPORTED FIELDS THROUGH
EXPORTED METHODS
As you might expect, now that we've converted the fields of Date to unexported, trying to
access them from the main package results in compile errors. This is true both when
trying to set the field values directly, and when using them in a struct literal.

But, we can still access the fields indirectly. Unexported variables, struct fields,
functions, methods, etc. can still be accessed by exported functions and methods in the
same package. So when code in the main package calls the exported SetYear method on a
Dat value, SetYear can update the Date's year struct field, even though it's unexported. The
e

exported SetMonth method can update the unexported month field. And so on.

If we modify main.go to use the setter methods, we'll be able to update a Date value's
fields:
Unexported variables, struct fields, functions, and methods can still be
accessed by exported functions and methods in the same package.

If we update main.go to call SetYear with an invalid value, we'll get an error when we run
it:

Now that a Date value's fields can only be updated via its setter methods, programs are
protected against accidentally entering invalid data.
Ah, that's right. We provided setter methods that let us set Date fields, even though
those fields are unexported from the calendar package. But we haven't provided any
methods to get the field values.

We can print an entire Date struct. But if we try to update main.go to print an individual
Dat field, we won't be able to access it!
e

GETTER METHODS
As we've seen, methods whose main purpose is to set the value of a struct field or
variable are called "setter methods". And as you might expect, methods whose main
purpose is to get the value of a struct field or variable are called getter methods.

Compared to the setter methods, adding getter methods to the Date type will be easy.
They don't need to do anything except return the field value when they're called.

By convention, a getter method's name should be the same as the name of the field or
variable it accesses. (Of course, if you want the method to be exported, its name will
need to start with a capital letter.) So Date will need a Year method to access the year field,
a Month method for the month field, and a Day method for the day field.

Getter methods don't need to modify the receiver at all, so we could use a direct Date
value as a receiver. But if any method on a type takes a pointer receiver, convention
says that they all should, for consistency's sake. Since we have to use a pointer receiver
for our setter methods, we use a pointer for the getter methods as well.

With the changes to date.go complete, we can update main.go to set all the Date fields, then
use the getter methods to print them all out.

ENCAPSULATION
The practice of hiding data in one part of a program from code in another part is known
as encapsulation, and it's not unique to Go. Encapsulation is valuable because it can
be used to protect against invalid data (as we've seen). Also, you can change an
encapsulated portion of a program without worrying about breaking other code that
accesses it, because direct access isn't allowed.

Many other programming languages encapsulate data within classes. (Classes are a
concept similar, but not identical, to a type). In Go, data is encapsulated within
packages, using unexported variables, struct fields, functions, or methods.

Encapsulation is used far more frequently in other languages than it is in Go. In some
languages it is conventional to define getters and setters for every field, even when
accessing those fields directly would work just as well. Go developers generally only rely
on encapsulation when it's necessary, such as when field data needs to be validated by
setter methods. In Go, if you don't see a need to encapsulate a field, it's generally okay
to export it and allow direct access to it.

THERE ARE NO DUMB QUESTIONS


Q: Many other languages don't allow access to encapsulated values outside
of the class where they're defined. Is it safe for Go to allow other code in
the same package to access unexported fields?

A: Generally, all the code in a package is the work of a single developer (or group of
developers). Generally all the code in a package has a similar purpose, as well. The
authors of code within the same package are most likely to need access to unexported
data, and they're also likely to only use that data in valid ways. So, yes, sharing
unexported data with the rest of the package is generally safe.
Code outside the package is likely to be written by other developers, but that's okay
because the unexported fields are hidden from them, so they can't accidentally change
their values to something invalid.

Q: I've seen other languages where the name of every getter method started
with "Get", as in GetName, GetCity, etc. Can I do that in Go?

A: The Go language will allow you to do that, but you shouldn't. The Go community has
decided on a convention of leaving the Get prefix off of getter method names. Including
it would only lead to confusion for your fellow developers!
Go still uses a Set prefix for setter methods, just like many other languages, because it's
needed to distinguish setter method names from getter method names for the same
field.
Bear with us; we'll need two pages to fit all the code for this exercise...
Fill in the blanks to make the following changes to the Coordinates type:

• Update its fields so they're unexported.

• Add getter methods for each field. (Be sure to follow the convention: a getter method's
name should be the same as the name of the field it accesses, with capitalization if the
method needs to be exported.)

• Add valdation to the setter methods. SetLatitude should return an error if the passed­in
value is less than -90 or greater than 90. SetLongitude should return an error if the new
value is less than -180 or greater than 180.

Next, update the main package code to make use of the revised Coordinates type.
• For each call to a setter method, store the error return value.

• If the error is not nil, use the log.Fatal function to log the error message and exit.

• If there were no errors setting the fields, call both getter methods to print the field
values.

The completed code should produce the output shown when it runs. (The call to
Set
Lati
tu  should be successful, but we're passing an invalid value to SetLongitude, so it
de

should log an error and exit at that point.)

EMBEDDING THE DATE TYPE IN AN EVENT TYPE


That shouldn't take much work. Remember how we embedded an Address struct type
within two other struct types back in Chapter 8?

The Address type was considered "embedded" because we used an anonymous field (a
field with no name, just a type) in the outer struct to store it. This caused the fields of
 to be promoted to the outer struct, allowing us to access fields of the inner struct
Address

as if they belonged to the outer struct.

Since that strategy worked so well before, let's define an Event type that embeds a Date
with an anonymous field.

Create another file within the calendar package folder, named event.go. (We could put it
within the existing date.go field, but this organizes things a bit more neatly.) Within that
file, define an Event type with two fields: a Title field with a type of string, and an
anonymous Date field.
UNEXPORTED FIELDS DON'T GET PROMOTED
Embedding a Date in the Event type will not cause the Date fields to be promoted to the
, though. The Date fields are unexported, and Go doesn't promote unexported fields
Event

to the enclosing type. That makes sense; we made sure the fields were encapsulated so
they can only be accessed through setter and getter methods, and we don't want that
encapsulation to be circumvented through field promotion.

In our main package, if we try to set the month field of a Date through its enclosing Event,
we'll get an error:

And of course, using dot operator chaining to retrieve the Date field and then access
fields on it directly won't work, either. You can't access a Date value's unexported fields
when it's by itself, and you can't access its unexported fields when it's part of an Event,
either.

So does that mean we won't be able to access the fields of the Date type, if it's embedded
within the Event type? Don't worry; there's another way!

EXPORTED METHODS GET PROMOTED JUST LIKE


FIELDS
If you embed a struct type within another struct type, the embedded type's exported
fields get promoted to the outer type. In the same way, if you embed a type with
exported methods within a struct type, its methods will be promoted to the outer type,
meaning you can call the methods as if they were defined on the outer type. (Only
defined types with an underlying struct type can have fields, but any defined type can
have methods, so the embedded type doesn't have to have an underlying struct type.)

Here's a package that defines two types. MyType is a struct type. MyType embeds a second
type, EmbeddedType, as an anonymous field.

Because EmbeddedType defines an exported method (named ExportedMethod), that method is
promoted to MyType, and can be called on MyType values.

As with unexported fields, unexported methods are not promoted. You'll get an error if
you try to call one.
Our Date fields weren't promoted to the Event type, because they're unexported. But the
getter and setter methods on Date are exported, and they do get promoted to the Event
type!

That means we can create an Event value, and then call the getter and setter methods for
the Date directly on the Event. That's just what we do in the updated main.go code below.
As always, the exported methods are able to access the unexported Date fields for us.

And if you prefer to use dot operator chaining to call methods on the Date value directly,
you can do that too:

ENCAPSULATING THE EVENT TITLE FIELD


Because the Event struct's Title field is exported, we can still access it directly:
This exposes us to the same sort of issues that we had with the Date fields, though. For
example, there's no limit on the length of the Title string:

It seems like a good idea to encapsulate the title field as well, so we can validate new
values. Here's an update to the Event type that does so. We change the field's name to
 so it's unexported, then add getter and setter methods. The RuneCountInString
title

function from the unicode/utf8 package is used to ensure there aren't too many runes
(characters) in the string.

PROMOTED METHODS LIVE ALONGSIDE THE


OUTER TYPE'S METHODS
Now that we've added setter and getter methods for the title field, our programs can
report an error if a title longer than 30 characters is used. An attempt to set a 39­
character title causes an error to be returned:
The Event type's Title and SetTitle methods live alongside the methods promoted from
the embedded Date type. Importers of the calendar package can treat all the methods as if
they belong to the Event type, without worrying about which type they're actually
defined on.

OUR CALENDAR PACKAGE IS COMPLETE!

Method promotion allows you to easily use one type's methods as if they belonged to
another. You can use this to compose types that combine the methods of several other
types. This can help you keep your code clean, without sacrificing convenience!

We completed the code for the Coordinates type in a previous exercise. You won't need to
make any updates to it this time; it's just here for reference. On the next page, we're
going to embed it in the Landmark type (which we also saw back in Chapter 8), so that its
methods are promoted to Landmark.

Here's an update to the Landmark type. We want its name field to be encapsulated,
accessible only by a Name getter method and a SetName setter method. SetName should return
an error if its argument is an empty string, or set the name field and return a nil error
otherwise. Landmark should also have an anonymous Coordinates field, so that the methods
of Coordinates are promoted to Landmark.

Fill in the blanks to complete the code for the Landmark type.
If the blanks in the code for Landmark are completed correctly, the code in the main
package should run, and produce the output shown.

YOUR GO TOOLBOX

That's it for Chapter 10! You’ve added encapsulation and embedding to
your toolbox.
 BULLET POINTS

 In Go, data is encapsulated within packages, using unexported package variables or
struct fields.

 Unexported variables, struct fields, functions, methods, etc. can still be accessed by
exported functions and methods in the same package.

 The practice of ensuring that data is valid before accepting it is known as "data
validation".

 A method that is primarily used to set the value of an encapsulated field is known as
a "setter method". Setter methods often include validation logic, to ensure the new
value being provided is valid.
 Since setter methods need to modify their receiver, their receiver parameter should
have a pointer type.

 A method that is primarily used to get the value of an encapsulated field is known
as a "getter method".

 Methods defined on an outer struct type live alongside methods promoted from an
embedded type.

 An embedded type's unexported methods don't get promoted to the outer type.

We need to add setter methods to the Coordinates type for each of its fields. Fill in the
blanks in the coordinates.go file below, so that the code in main.go will run and produce
the output shown.
Your goal with updating this code was to encapsulate the fields of the Coordinates type,
and add validation to its setter methods.

• Update the fields of Coordinates so they're unexported.

• Add getter methods for each field.

• Add valdation to the setter methods. SetLatitude should return an error if the passed­in
value is less than -90 or greater than 90. SetLongitude should return an error if the new
value is less than -180 or greater than 180.

Your next task was to update the main package code to make use of the revised Coordinates
type.
• For each call to a setter method, store the error return value.

• If the error is not nil, use the log.Fatal function to log the error message and exit.

• If there were no errors setting the fields, call both getter methods to print the field
values.

The call to SetLatitude below is successful, but we're passing an invalid value to
Set
Long
it , so it logs an error and exits at that point.
ude

Here's an update to the Landmark type (which we also saw in Chapter 8). We want its
name field to be encapsulated, accessible only by getter and setter methods. The SetName
method should return an error if its argument is an empty string, or set the name field
and return a nil error otherwise. Landmark should also have an anonymous Coordinates
field, so that the methods of Coordinates are promoted to Landmark.
11 interfaces
History

Topics

Tutorials What Can You Do?


Offers & Deals

Highlights

Settings

Support

Sign Out

Sometimes you don't care about the particular type of a value.

You don't care about what it is. You just need to know that it will be able to do certain
things. That you'll be able to call certain methods on it. You don't care whether you
have a Pen or a Pencil, you just need something with a Draw method. You don't care
whether you have a Car or a Boat, you just need something with a Steer method.
That's what Go interfaces accomplish. They let you define variables and function
parameters that will hold any type, as long as that type defines certain methods.

TWO DIFFERENT TYPES THAT HAVE THE SAME


METHODS
Remember audio tape recorders? (We suppose some of you will be too young.) They
were great, though. They let you easily record all your favorite songs together on a
single tape, even if they were by different artists. Of course, the recorders were usually
too bulky to carry around with you. If you wanted to take your tapes on the go, you
needed a separate, battery­powered tape player. Those usually didn't have recording
capabilities. Ah, but it was so great making custom mixtapes and sharing them with
your friends!

We're so overwhelmed with nostalgia that we've created a gadget package to help us
reminisce. It includes a type that simulates a tape recorder, and another type that
simulates a tape player.

The TapePlayer type has a Play method to simulate playing a song, and a Stop method to
stop the virtual playback.

The TapeRecorder type also has Play and Stop methods, and a Record method as well.
A METHOD PARAMETER THAT CAN ONLY ACCEPT
ONE TYPE
Here's a sample program that uses the gadget package. We define a playList function that
takes a TapePlayer value, and a slice of song titles to play on it. The function loops over
each title in the slice, and passes it to the TapePlayer's Play method. When it's done
playing the list, it calls Stop on the TapePlayer.

Then, in the main method, all we have to do is create the TapePlayer and the slice of song
titles, and pass them to playList.
The playList function works great with a TapePlayer value. You might hope that it would
work with a TapeRecorder as well. (After all, a tape recorder is basically just a tape player
with an extra record function.) But playList's first parameter has a type of TapePlayer. Try
to pass it an argument of any other type, and you'll get a compile error:

In this case, it does seem like the Go language's type safety is getting in our way, rather
than helping us. The TapeRecorder type defines all the methods that the playList function
needs, but we're being blocked from using it because playList only accepts TapePlayer
values.

So what can we do? Write a second, nearly­identical playListWithRecorder function that
takes a TapeRecorder instead?

Actually, Go offers another way...

INTERFACES
When you install a program on your computer, you usually expect the program to
provide you with a way to interact with it. You expect a word processor to give you a
place to type text. You expect a backup program to give you a way to select which files
to save. You expect a spreadsheet to give you a way to insert columns and rows for data.
The set of controls a program provides you so you can interact with it are often called
its "interface".

An interface is a set of methods that certain values are expected to have.

Whether you've actually thought about it or not, you probably expect Go values to
provide you with a way to interact with them, too. What's the most common way to
interact with a Go value? Through its methods.

In Go, an interface is defined as a set of methods that certain values are expected to
have. You can think of an interface as a set of actions you need a type to be able to
perform.

You define an interface type using the interface keyword, followed by curly braces
containing a list of method names, along with any parameters or return values the
methods are expected to have.

Any type that has all the methods listed in an interface definition is said to satisfy that
interface. A type that satisfies an interface can be used anywhere that interface is called
for.
The method names, parameter types (or lack thereof), and return value types (or lack
thereof) all need to match those defined in the interface. A type can have methods in
addition to those listed in the interface, but it mustn't be missing any, or it doesn't
satisfy that interface.

A type can satisfy multiple interfaces, and an interface can (and usually should) have
multiple types that satisfy it.

DEFINING A TYPE THAT SATISFIES AN INTERFACE


The code below sets up a quick experimental package, named mypkg. It defines an
interface type named MyInterface, with three methods. Then it defines a type named
 that satisfies MyInterface.
MyType

There are three methods required to satisfy MyInterface: a MethodWithoutParameters method,
a MethodWithParameter method that takes a float64 parameter, and a MethodWithReturnValue
method that returns a string.

Then we declare another type, MyType. The underlying type of MyType doesn't matter in this
example; we just used int. We define all the methods on MyType that it needs to satisfy
, plus one extra method that isn't part of the interface.
MyInterface
Many other languages would require us to explicitly say that MyType satisfies MyInterface.
But in Go, this happens automatically. If a type has all the methods declared in an
interface, then it can be used anywhere that interface is required, with no further
declarations needed.

Here's a quick program that will let us try mypkg out.

A variable declared with an interface type can hold any value whose type satisfies that
interface. This code declares a value variable with MyInterface as its type, then creates a
 value and assigns it to value. (Which is allowed, because MyType satisfies MyInterface.)
MyType

Then we call all the methods on that value that are part of the interface.
CONCRETE TYPES, ABSTRACT TYPES
All the types we've defined in previous chapters have been concrete types. A concrete
type specifies not only what its values can do (what methods you can call on them), but
also what they are: they specify the underlying type that holds the value's data.

An interface type is referred to as an abstract type. Abstract types don't describe what
a value is: they don't say what its underlying type is, or how its data is stored. They only
describe what a value can do: what methods it has.

Suppose you need to write down a quick note. In your desk drawer, you have values of
several concrete types: Pen, Pencil, and Marker. Each of these concrete types defines a Write
method, so you don't really care which type you grab. You just want a WritingInstrument:
an abstract type (interface type) that is satisfied by any concrete type with a Write
method.

ASSIGN ANY TYPE THAT SATISFIES THE INTERFACE


When you have a variable with an interface type, it can hold values of any type that
satisfies the interface.

Suppose we have Whistle and Horn types, each of which has a MakeSound method. We can
create a NoiseMaker interface that represents any type with a MakeSound method. If we
declare a toy variable with a type of NoiseMaker, we'll be able to assign either Whistle or Horn
values to it. (Or any other type that we later declare, as long as a has a MakeSound
method.)

We can then call the MakeSound method on any value assigned to the toy variable.
Although we don't know exactly what concrete type the value in toy is, we know what it
can do: make sounds. If its type didn't have a MakeSound method, then it wouldn't satisfy
the NoiseMaker interface, and we wouldn't have been able to assign it to the variable.

You can declare function parameters with interface types as well. (After all, function
parameters are really just variables too.) If we declare a play function that takes a
NoiseMa
k e, for example, then we can pass any value from a type with a MakeSound method
r

to play:

YOU CAN ONLY CALL METHODS DEFINED AS PART


OF THE INTERFACE
Once you assign a value to a variable (or method parameter) with an interface type, you
can only call methods that are specified by the interface on it.

Suppose we created a Robot class, which in addition to a MakeSound method, also has a Walk
method. We add a call to Walk in the play function, and pass a new Robot value to play.
But the code doesn't compile, saying that NoiseMaker values don't have a Walk method.

Why is that? Robot values do have a Walk method; the definition is right there!

But it's not a Robot value that we're passing to the play function; it's a NoiseMaker. What if
we had passed a Whistle or Horn to play instead? Those don't have Walk methods!

When we have a variable of an interface type, the only methods we can be sure it has
are the methods that are defined in the interface. And so, those are the only methods
Go allows you to call. (There is a way to get at the value's concrete type, so that you can
call more specialized methods. We'll look at that shortly.)

Note that it is just fine to assign a type that has other methods to a variable with an
interface type. As long as you don't actually call those other methods, everything will
work.
BREAKING STUFF IS EDUCATIONAL!

Here are a couple concrete types, Fan and CoffeePot. We also have an Appliance interface
with a TurnOn method. Fan and CoffeePot both have TurnOn methods, so they both satisfy the
 interface.
Appliance

That's why, in the main function, we're able to define an Appliance variable, and assign
both Fan and CoffeePot variables to it.

Make one of the changes below, and try to compile the code. Then undo your change,
and try the next one. See what happens!

type Fan string
func (f Fan) TurnOn() {
       fmt.Println("Spinning")
}

type CoffeePot string
func (c CoffeePot) TurnOn() {
       fmt.Println("Powering up")
}
func (c CoffeePot) Brew() {
       fmt.Println("Heating Up")
}

type Appliance interface {
       TurnOn()
}

func main() {
       var device Appliance
       device = Fan("Windco Breeze")
       device.TurnOn()
       device = CoffeePot("LuxBrew")
       device.TurnOn()
}

If you do this... ...the code will break because...

Call a method from the When you have a value in a variable with an interface

concrete type that isn't type, you can only call methods defined as part of

defined in the interface: that interface, regardless of what methods the

device
.Bre
w() concrete type had.

Remove the method that If a type doesn't satisfy an interface, you can't assign

satisfies the interface from a values of that type to variables that use that interface

type: as their type.

func(
cCo
ffeeP
ot)TurnOn(){

      
 fmt
.Pr
int
ln("
Powe
ring

up")

Add a new return value or If the number and types of all parameters and return

parameter on the method values don't match between a concrete type's method

that satisfies the interface: definition and the method definition in the interface,

func(
fFan
)TurnOn()er
ror{ then the concrete type does not satisfy the interface.

      
 fmt.
Print
ln("Spi
nni
ng"
)

      
 retu
rnnil

FIXING OUR PLAYLIST FUNCTION USING AN


INTERFACE
Let's see if we can use an interface to allow our playList function to work with the Play
and Stop methods on both of our concrete types: TapePlayer and TapeRecorder.
// TapePlayer type definition here
func (t TapePlayer) Play(song string) {
       fmt.Println("Playing", song)
}
func (t TapePlayer) Stop() {
       fmt.Println("Stopped!")
}
// TapeRecorder type definition here
func (t TapeRecorder) Play(song string) {
       fmt.Println("Playing", song)
}
func (t TapeRecorder) Record() {
       fmt.Println("Recording")
}
func (t TapeRecorder) Stop() {
       fmt.Println("Stopped!")
}

In our main package, we declare a Player interface. (We could define it in the gadget
package instead, but defining the interface in the same package where we use it gives us
more flexibility.) We specify that the interface requires both a Play method with a string
parameter, and a Stop method with no parameters. This means that both the TapePlayer
and TapeRecorder types will satisfy the Player interface.

We update the playList function to take any value that satisfies Player instead of
Tap
ePla
ye specifically. We also change the type of the player variable from TapePlayer to
r

Pla . This allows us to assign either a TapePlayer or a TapeRecorder to player. We then pass
yer

values of both types to playList!
If a type declares methods with pointer receivers, then you'll only be
able to use pointers to that type when assigning to interface variables.

The toggle method on the Switch type below has to use a pointer receiver so it
can modify the receiver.

package main

import "fmt"

type Switch string
func (s *Switch) toggle() {
       if *s == "on" {
              *s = "off"
       } else {
              *s = "on"
       }
       fmt.Println(*s)
}

type Toggleable interface {
       toggle()
}

func main() {
       s := Switch("off")
       var t Toggleable = s
       t.toggle()
       t.toggle()
}

But that results in an error when we assign a Switch value to a variable with
the interface type Toggleable:

When Go decides whether a value satisfies an interface, pointer methods
aren't included for direct values. But they are included for pointers. So the
solution is to assign a pointer to a Switch to the Toggleable variable, instead of a
direct Switch value:

Make that change, and the code should work correctly.

THERE ARE NO DUMB QUESTIONS


Q: Should interface type names begin with a capital letter, or a lower­case
letter?

A: The rules for interface type names are the same as the rules for any other type. If the
name begins with a lower­case letter, then the interface type will be unexported and
will not be accessible outside the current package. Sometimes you won't need to use the
interface you're declaring from other packages, so making it unexported is fine. But if
you do want to use it in other packages, you'll need to start the interface type's name
with a capital letter, so that it's exported.

The code at the right defines Car and Truck types, each of which have Accelerate, Brake,
and Steer methods. Fill in the blanks to add a Vehicle interface that includes those
three methods, so that the code in the main function will compile and produce the
output shown.

TYPE ASSERTIONS
We've defined a new TryOut function that will let us test the various methods of our
T
a pePla
ye and TapeRecorder types. TryOut has a single parameter with the Player interface as
r

its type, so that we can pass in either a TapePlayer or TapeRecorder.
Within TryOut, we call the Play and Stop methods, which are both part of the Player
interface. We also call the Record method, which is not part of the Player interface, but is
defined on the TapeRecorder type. We're only passing a TapeRecorder value to TryOut for now,
so we should be fine, right?

Unfortunately, no. We saw earlier that if a value of a concrete type is assigned to a
variable with an interface type (including function parameters), then you can only call
methods on it that are part of that interface, regardless of what other methods the
concrete type has. Within the TryOut function, we don't have a TapeRecorder value (the
concrete type), we have a Player value (the interface type). And the Player interface
doesn't have a Record method!

We need a way to get the concrete type value (which does have a Record method) back.

Your first instinct might be to try a type conversion to convert the Player value to a
T
a pe
R ec
orde value. But type conversions aren't meant for use with interface types, so that
r

generates an error. The error message suggests trying something else:

A "type assertion"? What's that?

When you have a value of a concrete type assigned to a variable with an interface type, a
type assertion lets you get the concrete type back. It's kind of like a type conversion.
Its syntax even looks like a cross between a method call and a type conversion. After an
interface value, you type a dot, and a pair of parentheses with the original type. (Or
rather, what you're asserting the value's original type is.)
In plain language, the type assertion above says something like "I know this variable
uses the interface type NoiseMaker, but I'm pretty sure this NoiseMaker is actually a Robot".

Once you've used a type assertion to get a value of a concrete type back, you can call
methods on it that are defined on that type, but aren't part of the interface.

This code assigns a Robot to a NoiseMaker interface value. We're able to call MakeSound on the
, because it's part of the interface. But to call the Walk method, we need to use a
NoiseMaker

type assertion to get a Robot value. Once we have a Robot (rather than a NoiseMaker), we can
call Walk on it.

TYPE ASSERTION FAILURES


Previously, our TryOut function wasn't able to call the Record method on a Player value,
because it's not part of the Player interface. Let's see if we can get this working using a
type assertion.

Just like before, we pass a TapeRecorder to TryOut, where it gets assigned to a parameter
that uses the Player interface as its type. We're able to call the Play and Stop methods on
the Player value, because those are both part of the Player interface.

Then, we use a type assertion to convert the Player back to a TapeRecorder. And we call
Rec  on the TapeRecorder value instead.
ord

Everything seems to be working great... with a TapeRecorder. But what happens if we try
to pass a TapePlayer to TryOut? How well will that work, considering we have a type
assertion that says the parameter to TryOut is actually a TapeRecorder?

Everything compiles successfully, but when we try to run it, we get a runtime panic! As
you might expect, trying to assert that a TapePlayer is actually a TapeRecorder did not go
well. (It's simply not true, after all.)

AVOIDING PANICS WHEN TYPE ASSERTIONS FAIL


If a type assertion is used in a context that expects only one return value, then if the
original type doesn't match the type in the assertion, the program will panic at run time
(not when compiling):
If a type assertions are used in a context where multiple return values are expected,
they have a second, optional return value that indicates whether the assertion was
successful or not. (And the assertion won't panic if it's unsuccessful.) The second value
is a bool, and it will be true if the value's original type was the asserted type, or false if
not. You can do whatever you want with this second return value, but by convention it's
usually assigned to a variable named ok. (This is similar to accessing a map, which also
has an optional return value that lets you tell zero values apart from explicitly assigned
values.)

Here's an update to the above code that assigns the results of the type assertion to a
variable for the concrete type's value, and a second ok variable. It uses the ok value in an
 statement to determine whether it can safely call Record on the concrete value
if

(because the Player value had an original type of TapeRecorder), or if it should skip doing
so (because the Player had some other concrete value).

In this case, the concrete type was TapePlayer, not TapeRecorder, so the assertion is
unsuccessful, and ok is false. The if statement's else clause runs, printing "Playerwasnot
aTapeR
e cord
er". A runtime panic is averted.

When using type assertions, if you're not absolutely sure which original type is behind
the interface value, then you should use the optional ok value to handle cases where it's
a different type than you expected, and avoid a runtime panic.

TESTING TAPEPLAYERS AND TAPERECORDERS


USING TYPE ASSERTIONS
Let's see if we can use what we've learned to fix our TryOut function for TapePlayer and
T
apeRec
o rde values. Instead of ignoring the second return value from our type assertion,
r

we'll assign it to an ok variable. The ok variable will be true if the type assertion is
successful (indicating the recorder variable holds a TapeRecorder value, ready for us to call
R
ecor on it), or false otherwise (indicating it's not safe to call Record). We wrap the call to
d

the Record method in an if statement to ensure it's only called when the type assertion is
successful.
As before, in our main function, we first call TryOut with a TapeRecorder value. TryOut takes
the Player interface value it receives, and calls the Play and Stop methods on it. The
assertion that the Player value's concrete type is TapeRecorder succeeds, and the Record
method is called on the resulting TapeRecorder value.

Then, we call TryOut again with a TapePlayer. (This is the call that halted the program
previously because the type assertion panicked.) Play and Stop are called, as before. The
type assertion fails, because the Player value holds a TapePlayer and not a TapeRecorder. But
because we're capturing the second return value in the ok value, the type assertion
doesn't panic this time. It just sets ok to false, which causes the code in our if statement
not to run, which causes Record not to be called. (Which is good, because TapePlayer
values don't have a Record method.)

Thanks to type assertions, we've got our TryOut function working with both TapeRecorder
and TapePlayer values!

POOL PUZZLE

Updated code from our previous exercise is at the right. We're creating a TryVehicle
method that calls all the methods from the Vehicle interface. Then, it should attempt a
type assertion to get a concrete Truck value. If successful, it should call LoadCargo on the
Tru
ck value.

Your job is to take code snippets from the pool and place them into the blank lines in
this code. Don't use the same snippet more than once, and you won’t need to use all
the snippets. Your goal is to make a program that will run and produce the output
shown.

THE "ERROR" INTERFACE


We'd like to wrap up the chapter by looking at a few interfaces that are built into Go.
We haven't covered these interfaces explicitly, but you've actually been using them all
along.

In Chapter 3, we learned how to create our own error values. We said, "An error value is
any value with a method named Error that returns a string."

That's right. The error type is just an interface! It looks something like this:

type error interface {
       Error() string
}

Declaring the error type as an interface means that if it has an Error method that returns
a string, it satisfies the error interface, and it's an error value. That means you can define
your own types and use them anywhere an error value is required!

For example, here's a simple defined type, ComedyError. Because it has an Error method
that returns a string, it satisfies the error interface, and we can assign it to a variable
with the type error.
If you need an error value, but also need to track more information about the error than
just an error message string, you can create your own type that satisfies the error
interface and stores the information you want.

Suppose you're writing a program that monitors some equipment to ensure it doesn't
overheat. Here's an OverheatError type that might be useful. It has an Error method, so it
satisfies error. But more interestingly, it uses float64 as its underlying type, allowing us
to track the degrees over capacity.

Here's a checkTemperature function that uses OverheatError. It takes the system's actual
temperature and the temperature that's considered safe as parameters. It specifies that
it returns a value of type error, not an OverheatError specifically, but that's okay because
O
verhea
tErro
r satisfies the error interface. If the actual temperature is over the safe
temperature, checkTemperature returns a new OverheatError that records the excess.
THERE ARE NO DUMB QUESTIONS
Q: How is it we've been using the error interface type in all these different
packages, without importing it? Its name begins with a lower­case letter.
Doesn't that mean it's unexported, from whatever package it's declared in?
What package is
er
ro declared in, anyway?
r

A: The error type is a "predeclared identifier", like int or string. And so, like other
predeclared identifiers, it's not part of any package. It's part of the "universe block",
meaning it's available everywhere, regardless of what package you're in.
Remember how there are if and for blocks, which are encompassed by function blocks,
which are encompassed by package blocks? Well, the universe block encompasses all
package blocks. That means you can use anything defined in the universe block from
any package, without importing it. And that includes error, and all other predeclared
identifiers.

THE STRINGER INTERFACE


Remember our Gallons, Liters, and Milliliters types, which we created back in Chapter 9
to distinguish between various units for measuring volume? We're discovering that it's
not so easy to distinguish between them after all. Twelve gallons is a very different
amount than twelve liters or twelve milliliters, but they all look the same when printed.
If there are too many decimal places of precision on a value, that looks awkward when
printed, too.
You can use Printf to round the number off and add an abbreviation indicating the unit
of measure, but doing that every place you need to use these types would quickly get
tedious.

That's why the fmt package defines the fmt.Stringer interface: to allow any type to decide
how it will be displayed when printed. It's easy to set up any type to satisfy Stringer; just
define a String() method that returns a string. The definition looks like this:

For example, here we've set up this CoffeePot type to satisfy Stringer:

Many functions in the fmt package check whether the values passed to them satisfy the
 interface, and call their String methods if so. This includes the Print, Println, and
Stringer

 functions and more. Now that CoffeePot satisfies Stringer, we can pass CoffeePot
Printf

values directly to these functions, and the return value of the CoffeePot's String method
will be used in the output:
Now for a more serious use of this interface type. Let's make our Gallons, Liters, and
Mil
lili
te  types satisfy Stringer. We'll move our code to format their values to String
rs

methods associated with each type. We'll call the Sprintf function instead of Printf, and
return the resulting value.

Now, any time we pass Gallons, Liters, and Milliliters values to Println (or most other fmt
functions), their String methods will be called, and the return values used in the output.
We've set up a useful default format for printing each of these types!

THE EMPTY INTERFACE

Good question! Let's run godoc to bring up the documentation for fmt.Println and see
what type its parameters are declared as...
As we saw in Chapter 6, the ... means that it's a variadic function, meaning it can take
any number of parameters. But what's this interface{} type?

Remember, an interface declaration specifies the methods that a type is required to
have in order to satisfy that interface. For example, our NoiseMaker interface is satisfied
by any type that has a MakeSound method.

type NoiseMaker interface {
       MakeSound()
}

But what would happen if we declared an interface type that didn't require any methods
at all? It would be satisfied by any type! It would be satisfied by all types!

type Anything interface {
}

The type interface{} is known as the empty interface, and it's used to accept values of
any type. The empty interface doesn't have any methods that are required to satisfy it,
and so every type satisfies it.

If you declare a function that accepts a parameter with the empty interface as its type,
then you can pass it values of any type as an argument:

The empty interface doesn't require any methods to satisfy it, and so it's
satisfied by all types.
But don't rush out and start using the empty interface for all your function parameters!
If you have a value with the empty interface as its type, there's not much you can do
with it.

Most of the functions in fmt accept empty­interface values, so you can pass it on to
those:

But don't try calling any methods on an empty­interface value! Remember, if you have
a value with an interface type, you can only call methods on it that are part of the
interface. And the empty interface doesn't have any methods. That means there are no
methods you can call on a value with the empty interface type!

To call methods on a value with the empty interface type, you'd need to use a type
assertion to get a value of the concrete type back.

And by that point, you're probably better off writing a function that accepts only that
specific concrete type.

So there are limits to the usefulness of the empty interface when defining your own
functions. But you'll use the empty interface all the time with the functions in the fmt
package, and in other places too. The next time you see an interface{} parameter in a
function's documentation, you'll know exactly what it means!

When you're defining variables or function parameters, often you'll know exactly what
the value you'll be working with is. You'll be able to use a concrete type like Pen, Car, or
Whi . Other times, though, you only care about what the value can do. In that case,
stle

you're going to want to define an interface type, like WritingInstrument, Vehicle, or
Noi
seMa
ke.
r

You'll define the methods you need to be able to call as part of the interface type. And
you'll be able to assign to your variables or call your functions without worrying about
the concrete type of your values. If it has the right methods, you'll be able to use it!

YOUR GO TOOLBOX

That's it for Chapter 11! You’ve added interfaces to your toolbox.

 BULLET POINTS
 A concrete type specifies not only what its values can do (what methods you can call
on them), but also what they are: they specify the underlying type that holds the
value's data.

 An interface type is an abstract type. Interfaces don't describe what a value is: they
don't say what its underlying type is, or how its data is stored. They only describe
what a value can do: what methods it has.

 An interface definition needs to contain a list of method names, along with any
parameters or return values those methods are expected to have.

 To satisfy an interface, a type must have all the methods the interface specifies.
Method names, parameter types (or lack thereof), and return value types (or lack
thereof) all need to match those defined in the interface.

 A type can have methods in addition to those listed in the interface, but it mustn't
be missing any, or it doesn't satisfy that interface.

 A type can satisfy multiple interfaces, and an interface can have multiple types that
satisfy it.

 Interface satisfaction is automatic. There is no need to explicitly declare that a
concrete type satisfies an interface in Go.

 When you have a variable of an interface type, the only methods you can call on it
are those defined in the interface.

 If you've assigned a value of a concrete type to a variable with an interface type, you
can use a type assertion to get the concrete type value back. Only then can you call
methods that are defined on the concrete type (but not the interface.)

 Type assertions return a second bool value that indicates whether the assertion was
successful.
c
ar,ok:=vehicle
.(Car
)
POOL PUZZLE SOLUTION

You might also like