Errata

Learning Go

Errata for Learning Go

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released. If the error was corrected in a later version or reprint the date of the correction will be displayed in the column titled "Date Corrected".

The following errata were submitted by our customers and approved as valid errors by the author or editor.

Color key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted By Date submitted Date corrected
Page 14
1st paragraph

(The .PHONY line keeps make from getting confused if you ever create a directory in your project with the same name as a target.) is what is written on the book.

No.
It will work just fine if you create a directory with the same name, it won't work if you create a file with the same name. Try it out.

Corrected version should be -
The .PHONY line is created to prevent 'make' from getting confused if you create a file with the same name as the target not directory.

Note from the Author or Editor:
Thanks for reaching out! I just double checked and with the version of make on my Mac, either a file or a directory will cause make to do the wrong thing if .PHONY isn't specified. I tested with a project that contains a target called 'fmt' that runs 'go fmt':

% make fmt
go fmt ./...
% touch fmt
% make fmt
make: `fmt' is up to date.
% rm fmt
% make fmt
go fmt ./...
% mkdir fmt
% make fmt
make: `fmt' is up to date.
% rmdir fmt
% make fmt
go fmt ./...

There won't be any more updates to the 1st edition of Learning Go, but in the 2nd edition (due out January 2024), it says:

The .PHONY line keeps make from getting confused if there is a directory or file in your project with same name as one of the listed targets.

Mainak Biswas  Dec 23, 2023 
Page pg 169
sample code, StatusErr struct declaration

Unwrap method declaration has a receiver of type "StatusError". It should be "StatusErr"

Note from the Author or Editor:
This has been fixed in the second edition.

Allan Olweny  Jun 03, 2023 
Page 79
First paragraph after code output

When describing the feature of the `switch` statement where you can declare a scoped variable, the text reads "Also like an `if` statement, you can declare a variable that's scoped to all of the branches of the `switch` statement. In our case, we are scoping the variable `word` to all of the cases in the `switch` statement."

In Example 4-19, the `word` variable is scoped by the `for` loop. The variable that is scoped in the `switch` statement is `size`.

Note from the Author or Editor:
This has been fixed in the 2nd edition.

Jeremy  Jan 24, 2023 
Page Page 287
1st word.

(This is another trivial error. Sorry)

"Lets take a look at an example" ->
"Let's take a look at an example"

Note from the Author or Editor:
This has been fixed in the 2nd edition.

Jon Forrest  Dec 25, 2022 
Page Page 199
2nd paragraph

"It’s FOSS has a good resource for learning more about the various
kinds of open source licenses."

There are some missing words in this sentence, or "It's" should be "Its".

Note from the Author or Editor:
"It's FOSS" is the name of the web site.

The sentence is wrong and should read:

"It’s FOSS is a good resource for learning more about the
various kinds of open source licenses."

Jon Forrest  Dec 25, 2022 
Page Chapter 11, page 235
Implementation of buildGZipReader

The function buildGZipReader is listed as an example of how to properly wrap instances of io.Reader. I believe this code has a bug.

There are three paths out of this function:

1. The call to os.Open could return an error, which will cause us to return early
2. The call to gzip.NewReader could return an error, which will cause us to return early
3. Both calls could succeed, which will cause us to return with a successful result

The bug is in the second case. At the point of the early return, the *File returned by os.Open is still, well, open. When we return here, that *File will become garbage, but it won't get cleaned up until the garbage collector runs. So the file will remain open for an indefinite amount of time, possibly not getting closed until the process ends.

In the case of an early return from case #2, there should be a call to `r.Close()` before returning. 

Note from the Author or Editor:
That's a good point. There should be an r.Close() inside the second if err != nil line. Good catch!

Daniel Yankowsky  Nov 19, 2022 
Page 340
first non code paragraph

"Now we need a function that matches the OrderedFunc definition."

Instead of saying "OrderedFunc", I think we need a function that matches "OrderableFunc", correct?

I don't see "OrderedFunc" mentioned in the few pages before the paragraph either. If this isn't a typo, then I would like to learn what's going on.

Note from the Author or Editor:
You are correct, OrderedFunc in the sentence "Now we need a function that matches the OrderedFunc definition." should have been OrderableFunc.

DAVID KAO  Oct 15, 2022 
Page N/A
Chapter 6, last paragraph

Arden Labs -> Ardan Labs

Note from the Author or Editor:
How embarrassing! Yes, please correct "Arden Labs" to "Arden Labs" in chapter 6.

This will be fixed in the next edition of the book.

Josta Yee  May 19, 2022 
Page 27
1st code sample and 1st paragraph

The code sample provided only sets variables x, y, z, and d, and does not print anything. However, the following paragraph says "When you run this code, it prints out...". It looks like there was a missing print statement.

Note from the Author or Editor:
You are correct. There should be an additional print statement after the type conversion declarations.

On page 27, at the end of Example 2-2, there should be an additional line in the example:

fmt.Println(z, d)

Keegan Dahm  Apr 18, 2022  May 13, 2022
Page 333
line 4

The the following code does not compile on "The Go2Go Playground" (and "The Go Playground", either).

https://gotipplay.golang.org/p/cCRlrSj1YJd

Please correct so that it can be executed using Go 1.18!!

Note from the Author or Editor:
Unfortunately, the Type Parameters Proposal ("generics") changes between the time the book was published and generics were released in Go 1.18. A revised chapter is in final review. The online version will be automatically updated and a PDF of the updated chapter will be made available for everyone to download.

Anonymous  Mar 31, 2022  May 13, 2022
Page Online
Chapter 1

The book OREILLY Learning Go really helped me in learning the
concepts, but it is missing a basic step at chapter-1. due to which my
other steps were producing errors. After a long search I found that in the
chapter-1 setting up the environment you have missed creating a mod file by
initiating it. due to which i was facing errors in "make" execution saying
"module not in the directory". I am sending this thinking that it will be
helpful to you when you release revised version of this book.

Note from the Author or Editor:
Thanks for the feedback!

Unfortunately, this was a change to the go tooling that was made after the book went to press. If you don't have a .mod file and you use ./... with go fmt, go build, go run, go test, or other go tool subcommands, you get the error message:

pattern ./...: directory prefix . does not contain main module or its selected dependencies

The go build and go run calls in chapter 1 specify hello.go explicitly, so you don't get the error with them, but the Makefile does fail.

On page 14, the sentence:

Once the Makefile is in the ch1 directory, type:

Should be replaced with:

Before you can use this Makefile, you need to make this project a Go module. We'll cover modules in Chapter 9, but for now, change to the ch1 directory and type in the following command:

go mod init ch1

Now you can use the Makefile. Type:

VIVEK S (submitted by O'Reilly customer support for user)  Feb 07, 2022  May 13, 2022
Page 145
1st code sample

The code sample ends with "return process(r)", then "return nil". Surely the second return statement cannot be reached?

Note from the Author or Editor:
You are correct.

On page 145, in the second code block, the line:

return nil

Should be removed.

Mike Taylor  Jan 17, 2022  May 13, 2022
Page 245
5th paragraph

The text reads:
"We're going to store this data into our t variable, one JSON object at a time."

The set up and hence type of the t variable has been missed out in the immediate vicinity of the text or in the book's preceding pages. The example code on the Go playground has set up the variable (as an anonymous version of the Person struct on page 244). The text would benefit from either initialising t explicitly, or saying what variable it is.

Note from the Author or Editor:
You are correct. The definition of t was removed from the book to save a few lines (the book was a bit longer than was originally intended), but the removal of the definition of t makes the example far less clear. It should be restored.

On page 245, the code sample that starts with:

dec := json.NewDecoder(strings.NewReader(data))

should have the following lines prepended:

var t struct {
Name string `json:"name"`
Age int `json:"age"`
}

Jonny N  Jan 10, 2022  May 13, 2022
Page 226
5th paragraph

process written rather than processor (struct name).

This eliminates the inC and outC channels from process, a goroutine from launch, and the entire waitForC method.

Should read:

This eliminates the inC and outC channels from processor, a goroutine from launch, and the entire waitForC method.

Note from the Author or Editor:
You are correct. Thanks for catching this!

On page 226, the sentence:

This eliminates the inC and outC channels from process, a goroutine from launch, and the entire waitForC method.

Should be replaced with:

This eliminates the inC and outC channels from processor, a goroutine from launch, and the entire waitForC method.

(process should be replaced with processor)

Jonny N  Jan 06, 2022  May 13, 2022
Page 246
third line from the top

The link in https://oreil.ly/_LTZQ redirect is slightly wrong. The page is correct but the target does not exist, so jumping to the intended example does not work.

https://pkg.go.dev/encoding/json#example_Decoder_Decode_stream

corrected:
https://pkg.go.dev/encoding/json#example-Decoder.Decode-Stream

Note from the Author or Editor:
Thanks for catching this!

On page 246, the URL

https://oreil.ly/_LTZQ

should redirect to:

https://pkg.go.dev/encoding/json#example-Decoder.Decode-Stream

Sebastian Krämer  Dec 30, 2021  May 13, 2022
Page 195
First paragraph

The first paragraph on page 195 describes 'minimum version selection', which is explained as meaning "you always get the lowest version of a dependency that is declared to work in all of the go.mod files across all your dependencies.". In the example which follows however, it is explained that "it will choose version v1.2.3, as that is the most recent specified version". For me, "the most recent package" does not match the explanation that the minimum version will be chosen, as the minimum version declared by the package dependencies is v1.1.0.

In looking online for clarification, I discovered that I believe Go uses *minimal* version selection, as opposed to *minimum*. (https://go.dev/ref/mod#minimal-version-selection). From reading these docs, it appears that the example is correct, but the explanation that proceeds it is at least partially incorrect.

Note from the Author or Editor:
Thanks for catching this.

It is a typo that Minimal Version Selection was called Minimal Version Selection on page 194 and page 195.

I agree that the phrase "most recent specified version" is not great.

On pages 194 and 195, replace

Minimum Version Selection

with

Minimal Version Selection

On page 195, replace:

as that is the most recent specified version.

with:

as that, in the words of the https://go.dev/ref/mod#minimal-version-selection[Go Modules Reference], is the minimum version that satisfies all requirements.


Will Bicks  Dec 23, 2021  May 13, 2022
Page 117
last code sample on the page

The last code sample:

f := struct {
Name string `json:"name"`
Age int `json:"age"`
}

should be (missing last {})

f := struct {
Name string `json:"name"`
Age int `json:"age"`
}{}

Note from the Author or Editor:
Thanks for catching this!

You are correct

On page 117, in the last code sample, the line:

}

should be:

}{}

Andrey Hsiao  Dec 15, 2021  May 13, 2022
Page 240
last paragraph

"The Sub method uses the montonic clock..."

should be "monotonic"

Note from the Author or Editor:
Thanks for catching this!

On page 240, the word "montonic" should be "monotonic"

Sebastian Krämer  Dec 09, 2021  May 13, 2022
Page 78
Top of page (2nd printed line)

The line of code at the top of page 78, under section heading "Choosing the Right for Statement"

if i == len(evenVals)-2 {

Should in fact read:

if i == len(evenVals)-1 {

for the code to behave as described (otherwise it breaks the loop before printing penultimate entry in slice).

Note from the Author or Editor:
Thanks for catching this!

You are correct.

On page 78, the line:

if i == len(evenVals)-2 {

should read:

if i == len(evenVals)-1 {

Jonny N  Nov 28, 2021  May 13, 2022
Page 180
2nd paragraph after main.go listing

"We're imported three packages."

Should be "We're importing three packages." ?

Or "We"ve imported.." but in the next line there is another "we've.." sentence so that duplication chould be avoided, I think.

Note from the Author or Editor:
Thanks for catching this!

On page 180, the sentence:

We’re imported three packages.

Should be:

We’re importing three packages.

Sebastian Krämer  Nov 24, 2021  May 13, 2022
Page 181
4th paragraph

"We declare a package to be a staring point.."

I think that should read "starting point"?

Note from the Author or Editor:
Thanks for catching this!

Yes, that's a typo.

On page 181,

the word "starting" in the 4th paragraph should be "starting"

Sebastian Krämer  Nov 24, 2021  May 13, 2022
Page 154
-

Chapter 4: switch

"Also like an if statement, you can declare a variable that’s scoped to all of the branches of the switch statement. In our case, we are scoping the variable word to all of the cases in the switch statement."

I think "the variable word" here should be "the variable size".

Note from the Author or Editor:
Thanks for finding this! Yes, that's the wrong variable name.

On page 79 in the print edition, the sentence:

"In our case, we are scoping the variable word to all of the cases in the switch statement."

should read:

"In our case, we are scoping the variable size to all of the cases in the switch statement."

Anonymous  Nov 05, 2021  May 13, 2022
Page 463
last paragraph

This is a possible resolution of the error in the program 'context_cancel' in Chapter 12: Context, that I had reported earlier.

The function 'slowServer' in servers.go can be written as follows to avoid the error reported earlier:

func slowServer() *httptest.Server {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
select {
case <-ctx.Done():
case <-time.After(6 * time.Second):
}
//time.Sleep(6 * time.Second)
w.Write([]byte("Slow response"))
}))
return s
}

Note from the Author or Editor:
Hi Krishna,

I do like this fix, because it shows the context being used in a server. I don't think I can include it in a revision of the current edition, but what I'd like to do is use this in an example box between the end of the section on cancellation and the section on timers, with the text:

You probably noticed that the program didn't exit immediately when the error is triggered. That's because it is waiting for the `slowServer` to close. If you lengthen the timeout from 2 seconds to 6 seconds or more, you'll see an error message that starts with `httptest.Server blocked in Close after 5 seconds, waiting for connections`

If we rewrite `slowServer()` to properly handle context cancelation, we can shut it down immediately:

func slowServer() *httptest.Server {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
select {
case <-ctx.Done():
fmt.Println("server shut down")
return
case <-time.After(6 * time.Second):
w.Write([]byte("Slow response"))
}
}))
return s
}

Krishna  Oct 22, 2021  May 13, 2022
Page 273
The t.Errorf code under Reporting Test Failures

The line is:

t.Errorf("incorrect result: expected %d, got %s", 5, result)

The addNumbers function takes two ints and returns an int. In the t.Errorf line, the "%s" should be a "%d", shouldn't it?

Note from the Author or Editor:
Hello,

You are absolutely correct. Thanks for catching my mistake!

On page 273 in the print edition, change the line:

t.Errorf("incorrect result: expected %d, got %s", 5, result)

to

t.Errorf("incorrect result: expected %d, got %d", 5, result)

Leam Hall  Oct 20, 2021  May 13, 2022
Page 169
chapter 8 Wrapping errors

StatusErr struct fields named "err" is inconsistent in the example.

type StatusErr struct {
Status Status
Message string
err error <--------
In some cases, expecting}

func (se StatusErr) Error() string {
return se.Message
}

func (se StatusError) Unwrap() error {
return se.err
}
func LoginAndGetData(uid, pwd, file string) ([]byte, error) {
err := login(uid,pwd)
if err != nil {
return nil, StatusErr {
Status: InvalidLogin,
Message: fmt.Sprintf("invalid credentials for user %s",uid),
Err: err, <-------
}
}
data, err := getData(file)
if err != nil {
return nil, StatusErr {
Status: NotFound,
Message: fmt.Sprintf("file %s not found",file),
Err: err, <------------
}
}
return data, nil
}

Note from the Author or Editor:
Hello,

Yes, this is a mistake. There's some extraneous text in the example as well.

On page 169, the lines of code that say:

type StatusErr struct {
Status Status
Message string
err error
In some cases, expecting}

Should be replaced with:

type StatusErr struct {
Status Status
Message string
Err error
}

Also, the code:

func (se StatusError) Unwrap() error {
return se.err
}

should be replaced with:

func (se StatusError) Unwrap() error {
return se.Err
}

Ajay Kumar Mahar  Oct 15, 2021  May 13, 2022
Page 169
In first struct definition in code example on page

The last line of the StatusErr struct definition contains the text "In some cases, expecting":

type StatusErr struct {
Status Status
Message string
err error
In some cases, expecting}

should be:


type StatusErr struct {
Status Status
Message string
err error
}

Note from the Author or Editor:
Hello, yes, this is a typo in the book. Another errata noted that err should be capitalized.

On page 169, the text:

type StatusErr struct {
Status Status
Message string
err error
In some cases, expecting}

should be:

type StatusErr struct {
Status Status
Message string
Err error
}

Dino Fizzotti  Aug 22, 2021  May 13, 2022
Page 345
at the end of the section for "G"

In the Index, under the section for "G", "gotto statement" is a typo. It should be "goto statement".

This is the second line above the section "H" in the Index.

Note from the Author or Editor:
Yep, this is a typo.

On page 345:

gotto statement, 83-85

should be:

goto statement, 83-85

Anonymous  Aug 12, 2021  May 13, 2022
Page 179
First sentence

In the first sentence on the page, it says "(an identifier is the name of a variable, coonstant, type, function," - "coonstant" is a typo and should be "constant".

Note from the Author or Editor:
Thanks for finding this!

In the first line on page 179, the word "coonstant" should be "constant".

Anonymous  Aug 11, 2021  May 13, 2022
Page 26
Integer types paragraph

“Go provides both signed and unsigned integers in a variety of sizes, from one to four bytes.”

It should be “from one to eight bytes”

Note from the Author or Editor:
Thanks for finding this!

You are absolutely correct.

On page 20 in the print edition, the sentence:

Go provides both signed and unsigned integers in a variety of sizes, from one to four bytes.

should read:

Go provides both signed and unsigned integers in a variety of sizes, from one to eight bytes.

Damien Legros  Aug 03, 2021  May 13, 2022
Page 150
First code example

In the first code example the printed code differs from the Go Playground code (https://oreil.ly/_nUSw) on the last line.

In the book "fmt.Println(i2 + i)" results in an error "invalid operation: i2 + i (mismatched types MyInt and interface {})" whereas the Playground code only uses "fmt.Println(i2)" which is valid.

Note from the Author or Editor:
The example in the book says i2 + 1, not i2 + i. This does work correctly. However, the example on the Go Playground doesn't have the + 1. The correct link should be https://play.golang.org/p/pV8QT9WNszz .

On page 150 in the print edition, the URL pointed to by https://oreil.ly/_nUSw should be changed

from:

https://play.golang.org/p/_kbJZKt0Wnw

to:

https://play.golang.org/p/pV8QT9WNszz

Fabio La Micela  Jul 26, 2021  May 13, 2022
Page Chapter 7 -> "Interfaces Are Type-Safe Duck Typing" section -> Java example
Chapter 7 -> "Interfaces Are Type-Safe Duck Typing" section -> Java example

Chapter 7 -> "Interfaces Are Type-Safe Duck Typing" section -> Java example

The example has a minor error where program should actually call the process method on the logic field and not call the field as a method:

public void program() {
this.logic(data);
}

this.logic(data) should be this.logic.process(data)

Note from the Author or Editor:
Thanks for catching this! You are correct, the method call is wrong in the Java example.

On page 143 in the print edition, the line of code:

this.logic(data);

should read:

this.logic.process(data);

Indentation should remain the same.

Elisabeta Nitura  Jul 11, 2021  May 13, 2022
Page ??
Chapter 2

"uint16 0 to 65536" ->
"uint16 0 to 65535"

Note from the Author or Editor:
You are absolutely correct! Sorry about that mistake.

On page 20 in the print edition, the line in the chart that says:

uint16 0 to 65536

should read

uint16 0 to 65535

Jon Forrest  Jul 08, 2021  May 13, 2022
Page 189
Third block of code

in:

func MakeBar() Bar {
bar := Bar {
a: 20,
B: "Hello",
}
var f Foo = bar
fmt.Println(f.Hello())
return bar
}

Lines:

bar := Bar {
a: 20,
B: "Hello",
}

Should be:

bar := Bar {
x: 20,
S: "Hello",
}

Note from the Author or Editor:
Yep, I had renamed the fields in Foo from a to x and from B to S and didn't fix all of the code examples. Good catch!

On page 189, The block of code should read:

func MakeBar() Bar {
bar := Bar {
x: 20,
S: "Hello",
}
var f Foo = bar
fmt.Println(f.Hello())
return bar
}

The indentation should remain unchanged, just the field names a and B should become x and S, respectively.

Demetrios Vassiliades  Jun 28, 2021  May 13, 2022
Page 3
Second block of code/commands

On Windows, the commands listed are as follows:
setx GOPATH %USERPROFILE%\go
setx path "%path%;%USERPROFILE%\bin"

Should the second command not be the following ? :

setx path "%path%;%GOPATH%\bin"

Note from the Author or Editor:
Absolutely right, the second setx should use GOPATH and not USERPROFILE.

On page 3, the line:

setx path "%path%;%USERPROFILE%\bin"

should read:

setx path "%path%;%GOPATH%\bin"

Brian Lynch  Jun 24, 2021  May 13, 2022
Page 8
third to one from last paras

On page 8 the golint tool is recommended (and it is mentioned & recommended again on page 285). Unfortunately golint is deprecated and no longer maintained. The recommended tool now appears to be statickcheck: https://staticcheck.io/

Note from the Author or Editor:
This is the danger when you write a chapter in 2019 and it is published in 2021. This is correct; golint is no longer maintained or recommended and staticcheck is the preferred replacement tool.

Updating this section to refer to static check would be a non-trivial change and would involve updating the index as well. I'll have to check with my editors to see if we can put in an update to fix this.

Mark Summerfield  Jun 11, 2021 
Page 293
First code quote

There is already an errata:

Safari Books Online
Chapter 13 Writing Tests
httptest
The change is on page 291.
Kurt Yu Mar 21, 2021 May 14, 2021

The only thing wrong is that the change is on page 293 not 291 (in a First Release print edition).

Note from the Author or Editor:
This is my first errata on an errata :-) The page number in the previous erratum was wrong. The rest of the comments stand. This isn't a bug, but this small change makes the code more clear. On page 293, replace

rw.Write([]byte("invalid expression: " + io.expression))

with:

fmt.Fprintf(rw, "expected expression '%s', got '%s'",
io.expression, expression)

Mark Summerfield  Jun 11, 2021  May 14, 2021
Page 242
2nd paragraph, last sentence

There is backslash in the phrase "if\the field names" between the word "if" and the word "the".

Note from the Author or Editor:
Correct, there is an errant backslash in that sentence.

On page 242, the final sentence of the second paragraph should read:

"Despite this default behavior, it's best to use the struct tag to specify the name of the field explicitly, even if the field names are identical."

Anonymous  Jun 04, 2021  May 13, 2022
Page 239
4th paragraph, first sentence

There is an extra "n" in "An" in the phrase "An moment of time".

Note from the Author or Editor:
Correct, there is a typo.

The first sentence of paragraph 3 on page 239 should be:

"A moment of time is represented with the time.Time type, complete with a time zone.

Anonymous  Jun 04, 2021  May 13, 2022
Page 230
second paragraph, first sentence

"mutexs" is mispelled.

Note from the Author or Editor:
Correct, there is a typo.

On page 230, the first sentence in the second paragraph should be:

"Like sync.WaitGroup and sync.Once, mutexes must never be copied."

Anonymous  Jun 04, 2021  May 13, 2022
Page 283
in the second code listing, the 6th line of code, (2nd if statement)

There appears to be a numeral '2' character in the line. It is the very last '2' character on the line and occurs after the curly brace:

if result != 65204 { 2

Note from the Author or Editor:
Correct, I think that was an old formatting code that was inadvertently left behind.

On page 283, in the code block at the bottom of the page, the line:

if result != 65204 { 2

should be:

if result != 65204 {

indentation should be identical; the trailing 2 should not be there.

Anonymous  Jun 04, 2021  May 13, 2022
Page 213
The firs sentence beside the bird icon at the very top of the page

"This doesn't meant" is not grammatical in the English language.

Note from the Author or Editor:
Correct, this is a typo. Thanks for finding it!

On page 213, The first sentence of the note should be:

This doesn't mean that you shouldn't ever have channels as function parameters or struct fields.

Anonymous  Jun 04, 2021  May 13, 2022
Page 274
last paragraph first sentence

There is an extra full stop or dot character in the first sentence of the last paragraph as the last character of "*testing.T." and directly before the word "is".

Note from the Author or Editor:
Correct, there shouldn't be a period after *testing.T

On page 274, the first sentence of the final paragraph should read:

The Cleanup method on *testing.T is used to clean up temporary resources created for a single test.

Anonymous  Jun 04, 2021  May 13, 2022
Chapter 10
Chapter 10 Putting Our Concurrent Tools Together

There are four cases. The first two read from the channels written to by our first two goroutines and populate the fields in inputC. If both of these cases execute, we exit the for-select loop and return the value of inputC and a nil error.

The second two cases handle error conditions.

The above line should read as "The next two cases handle error conditions."

Note from the Author or Editor:
Yes, agreed, it would read better as "next two" instead of "second two".

On page 226, the start of the second paragraph reads:

The second two cases handle error conditions.

It should say:

The next two cases handle error conditions.

S Deepak  Apr 23, 2021  May 14, 2021
Chapter 10
Chapter 10 How Channels Behave


Unbuffered, open Unbuffered, closed Buffered, open Buffered, closed Nil
Close Works PANIC Works, remaining values still there PANI PANIC

The field value corresponding to Close operation in the Buffered Closed state should be "PANIC" instead of "PANI"


Note from the Author or Editor:
This is a typo. In the chart on page 209, in the box for Closing a Closed, Buffered Channel, it should say PANIC instead of PANI

S Deepak  Apr 21, 2021  May 14, 2021
Printed
Page 91
Chapter 5 "Ignoring Returned Values" (second paragraph from bottom of the page)

While explaining how to ignore some returned values from functions that return multiple values, you have the wrong number of assignments for a function from the previous page. You write "For example, if we weren't going to read `remainder`, would write the assignment as `result, _ := divAndRemainder(5,2)`.

But divAndRemainder returns three values: the result of division, the remainder, and a possible error. So you need either `result, _, err := divAndRemainder(5,2)` or `result, _, _:= divAndRemainder(5,2)`.

This is relatively minor, but initially I wondered if `_` could be used as a soak to pick up a variadic number of ignored return values. (I.e., if `_` could automatically handle both the remainder and the possible error.) But the Go playground suggests that `_` cannot handle more than one item.

https://play.golang.org/p/78_txMBRRW_d

Note from the Author or Editor:
Thanks for catching this.

In the second to last paragraph on page 91, the last sentence should read:

For example, if we weren't going to read remainder, we would write the assignment as result, _, err := divAndRemainder(5, 2).

Peter Aronoff  Apr 18, 2021  May 14, 2021
Chapter 7
Chapter 7 Interfaces

The following text seems unclear

If the passed-in io.Reader implements also implemented io.ReaderFrom, wrapping it in a buffered reader prevents the optimization.

Should it be :
If the passed-in io.Reader also implements the "interface" io.ReaderFrom, wrapping it in a buffered reader prevents the optimization.

Note from the Author or Editor:
There's an extra word in the sentence.

On page 153, The sentence:

If the passed-in io.Reader implements also implemented io.ReaderFrom, wrapping it in a buffered reader prevents the optimization.


should read:

If the passed-in io.Reader also implemented io.ReaderFrom, wrapping it in a buffered reader prevents the optimization.

S Deepak  Apr 16, 2021  May 14, 2021
Chapter 5
Chapter 5 defer

The following code snippet

func getFile(name string) (*os.File, func(), error) {
file, err := os.Open(name)
if err != nil {
return nil, nil, err
}
return file, func() {
file.Close()
}, err
}
Shouldn't the last return have nil instead of err, like below:

return file, func() {
file.Close()
}, nil

Note from the Author or Editor:
While the printed code would work, you are right that it is not a best practice.

On page 103, the code sample that ends with:

}, err
}

should end with:
}, nil
}

S Deepak  Apr 14, 2021  May 14, 2021
Chapter 5
Chapter 5 Functions and Values

In the following:

The type of a function is built out of the keyword func and the types of the parameters and return values. This combination is called the signature of the function. Any function that has the exact same number and type of parameters meets the type signature

The last line doesn't include the return values in the signature. So it should actually read as " Any function that has the exact same number and type of parameters and also exact same number and type of return values meets the type signature"

Note from the Author or Editor:
Yes, the sentence could be clearer and indicate that the return values are part of the function signature.

On page 94, the sentence:

Any function that has the exact same number and type of parameters meets the type signature.

should be:

Any function that has the exact same number and types of parameters and return values meets the type signature.

S Deepak  Apr 14, 2021  May 14, 2021
Chapter 3
Chapter 3 Structs

There are two common situations where anonymous structs are handy. The first is when you translate external data into a struct or a struct into external data (like JSON or protocol buffers). This is called marshaling and unmarshaling data.


The 3rd sentence provides inverted explanation of sentence 2. Marshaling is when you are serializing the data to be sent across a network and unmarshaling is retrieving the serial data (JSON/XML/ protocol buffers) and converting into struct data structure of the language. I hope you are getting the point when I say the explanation inverted ( if you are to keep the 2nd sentence as it is the 3rd sentence will have to be rephrased to

"This is called unmarshaling and marshaling data." But like encode/decode ( you cannot decode before encode , similarly you cannot unmarshal unless something has got marshaled before), so the changing sentence 3 will not be good, so I think it will be to rephrase sentence 2 like "The first is when you translate a struct into a a form to be sent over the network ( called serialization using JSON, XML or protocol buffers) and then transforming the serialized data into struct."


Note from the Author or Editor:
Yes, it would be clearer if the order of operations in the second sentence matched the third.

On page 58, the sentence:

This is called marshaling and unmarshaling data.

Should be:

This is called unmarshaling and marshaling data.

S Deepak  Apr 12, 2021  May 14, 2021
Chapter 2
Chapter 2 FLOATING POINT TYPES

After IEEE 754

“While Go lets you use == and != to compare floats, don’t do it. Due to the inexact nature of floats, two floating point values might not be equal when you think they should be. Instead, define a minimum allowed variance and see if the difference between two floats is less than that. “

Shouldn’t it be maximum allowed variance and see if the difference between two floats is less than that.

Note from the Author or Editor:
This is an error.

On page 24, the sentences:

Instead, define a minimum allowed variance and see if the difference between two floats is less than that. This minimum value (sometimes called epsilon) depends on what your accuracy needs are; I can’t give you a simple rule.

Should read:

Instead, define a maximum allowed variance and see if the difference between two floats is less than that. This value (sometimes called epsilon) depends on what your accuracy needs are; I can’t give you a simple rule.

S Deepak  Apr 12, 2021  May 14, 2021
Printed
Page 71
Example 4-12 at the top of the page

In example 4-12, the final line in the `for` loop (`fmt.Println(i)` is indented one tab too far. It should line up with the three `if` statements. (Very minor but the point of the example is using `continue` to remove indentation, so...)

Note from the Author or Editor:
This indentation should be fixed.

On page 71, the 2nd to last line in example 4-12:

fmt.Println(i)

Should line up with the closing brace on the preceding line.

Peter Aronoff  Apr 09, 2021  May 14, 2021
7
Chapter 7 Type Assertions and Type Switches Note

Type conversions can be applied to both concrete types and interfaces and are checked at compilation type.

Should be '...compilation time...'

Note from the Author or Editor:
Thanks for finding this! Yes, the last word in the sentence should be "time".

On Page 151, in the note section at the top of the page, the sentence:

"Type conversions can be applied to both concrete types and interfaces and are checked at compilation type."

should read:

"Type conversions can be applied to both concrete types and interfaces and are checked at compilation time."

Anonymous  Apr 05, 2021  May 14, 2021
Printed
Page 52
"What Is a Hash Map?" section

So, there is a tiny typo in the last sentence of the second paragraph in the "What Is a Hash Map?" section.

"If there is already an identical key in the bucket, the previous value is **replace** with the new value" should be "If there is already an identical key in the bucket, the previous value is **replaced** with the new value."

Separately, I found this section confusing. I've read the second and third paragraphs in the section several times, and I still find it somewhat hard to follow how two non-unique keys (the result of a collision) are "stored in the bucket." (An added problem: in the middle of explaining that collisions can happen and yet both key-value pairs with identical keys are stored, you switch gears to mention that within the map if keys themselves—as opposed to the hashes of keys, I assume—are identical then the new key-value pair overwrites the old.)

It's very possible that I'm just being dense, but I find these two paragraphs hard to follow.

Note from the Author or Editor:
Thank you for catching the typo! On page 52, the line:

the previous value is replace with the new value.

should be:

the previous value is replaced with the new value.

As for finding the explanation unclear, I'm sorry that I didn't convey the concept properly. I was trying to be brief and apparently left some details unclear.

I have a simple hash map implemented at https://play.golang.org/p/AX-DbACV-fd .

Think of each bucket in a hash map as a slice. Each element of the slice contains a struct with both the key and the value.

When you add a key and a value to a hash map, you turn the key into a hashcode via a hash algorithm. You then take that number and mod it by the number of buckets in the hash map. This gives you an offset into the buckets array. You then iterate through all of elements of the slice at the offset and see if any of the elements have the same key. If they do, replace the element in the slice with a new struct that contains the provided key and value and then return. If there is no element with the same key, add the new key/value pair to the end of the slice.

To get from the hash map, you take the key, convert it to a hashcode, mod the number by the number of buckets, and then iterate through the slice at that offset to find an element in the slice with the same key. If you find it, return the value and true. If you don't, return an empty string and false.

To delete from the hash map, you take the key, convert it to a hashcode, mod the number by the number of buckets, and then iterate through the slice at that offset to find an element in the slice with the same key. If you find it, remove that element from the slice and return.

A collision happens when two different keys have the same hashcode or produce the same value when you mod their hashcode with the length of the bucket array. You can see that there are many more possible strings than there are buckets in the hash map, so collisions are inevitable.

Now, there are many other details that my example skips over; the actual implementation of the map data type is far more complicated than this. But I hope this makes it clear.

Peter Aronoff  Apr 04, 2021  May 14, 2021
Printed
Page 47
At the bottom of the page

In the last paragraph on the page, you say "The first call to `copy` copies the last two values in array `d` into slice `y`", but I think you mean "copies the **first** two values in array `d` into slice `y`. (That seems confirmed by the Go playground and the output you show on page 47: 5 and 6 are copied, not 7 and 8.

Note from the Author or Editor:
This is an error. On page 47, the sentence that starts:

The first call to copy copies the last two values

should read:

The first call to copy copies the first two values

Peter Aronoff  Apr 04, 2021  May 14, 2021
Printed, PDF, ePub, Mobi,
Page 18
3rd paragraph

In the last sentence of the third paragraph, "tten" should read "ten" in the phrase "by breaking up base tten numbers at the thousands."

Note from the Author or Editor:
Confirmed. This should be "ten".

Peter Aronoff  Mar 31, 2021  May 14, 2021
, Printed, PDF, ePub, Mobi, , Other Digital Version
Page Chapter 14 Here There Be Dragons
cgo

Example2 fails when using Ubuntu20 or CentOS7:

urt@ub20:~/GitHub/learning_go/cgo_examples/example2$ go run main.go
# command-line-arguments
/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_c2aa1e2acbbc_Cfunc_add':
/tmp/go-build/cgo-gcc-prolog:54: undefined reference to `add'
collect2: error: ld returned 1 exit status

I don't know any workaround to fix this because example.h has a dependency on _cgo_export.h which is nowhere to be found.

Note from the Author or Editor:
I just verified. It should be go build, not go run.

The text should make this clear for example1 and example 2.

Kurt Yu  Mar 26, 2021  May 14, 2021
, Printed, PDF, ePub, Mobi, , Other Digital Version
Page Chapter 14 Here There Be Dragons
cgo

For example1, using GitHub commit 7e840f1 and go 1.16, I still get an error whether using CentOS7 or Ubuntu 20:

kurt@ub20:~/GitHub/learning_go/cgo_examples/example1$ go run main.go
# command-line-arguments
/usr/bin/ld: $WORK/b001/_x002.o: in function `_cgo_faccc96a4219_Cfunc_multiply':
/tmp/go-build/cgo-gcc-prolog:74: undefined reference to `multiply'
collect2: error: ld returned 1 exit status

To fix the error, I used the following workaround. In main.go:
#cgo LDFLAGS: -lm -lmylib -L.

Then I executed the following:
urt@ub20:~/GitHub/learning_go/cgo_examples/example1$ gcc -c -o mylib.o mylib.c
kurt@ub20:~/GitHub/learning_go/cgo_examples/example1$ ls
main.go mylib.c mylib.h mylib.o
kurt@ub20:~/GitHub/learning_go/cgo_examples/example1$ ar rcs libmylib.a mylib.o
kurt@ub20:~/GitHub/learning_go/cgo_examples/example1$ ls
libmylib.a main.go mylib.c mylib.h mylib.o
kurt@ub20:~/GitHub/learning_go/cgo_examples/example1$ go run main.go
a: 3, b: 2, sum 5
5
10
200
kurt@ub20:~/GitHub/learning_go/cgo_examples/example1$

Note from the Author or Editor:
I just verified. It should be go build, not go run.

The text should make this clear for example1 and example 2.

On page 322, the line

$ go run main.go

should be replaced with

$ go build
$ ./example1

Kurt Yu  Mar 26, 2021  May 14, 2021
Chapter 14 Here There Be Dragons
cgo

When I run the first example on cgo, I get the following error:

[yukurt@c7 example1]$ go run main.go
# command-line-arguments
/tmp/go-build3204744771/b001/_x002.o: In function `_cgo_b1754505e405_Cfunc_multiply':
/tmp/go-build/cgo-gcc-prolog:74: undefined reference to `multiply'
/tmp/go-build3204744771/b001/_x002.o: In function `_cgo_b1754505e405_Cfunc_sqrt':
/tmp/go-build/cgo-gcc-prolog:92: undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

Would you know what step I am missing? I am using a CentOS7 machine.

Note from the Author or Editor:
I'm not sure why this built correctly on my Mac, but it does not build correctly on Linux without the following addition to the magic comment:

#cgo LDFLAGS: -lm

On page 321, this line should be inserted right before:

#include <stdio.h>

After making this change, it builds correctly on both Mac and Linux. I have updated the sample on GitHub.

Kurt Yu  Mar 25, 2021  May 14, 2021
ePub
Page Ch 3
Using maps as sets

In the note section of chapter 3, section Using maps as sets there's a small error where an empty struct value is written as struct{} instead of struct{}{}:

for _, v := range vals {
intSet[v] = struct{} // this should be struct{}{}
}

Note from the Author or Editor:
Good catch!

On page 56, the line:

intSet[v] = struct{}

should be changed to:

intSet[v] = struct{}{}

Elisabeta Nitura  Mar 23, 2021  May 14, 2021
Printed, PDF,
Page 286
code sample at top of page

There are two problems with this code sample. First, there are 4 values in data, and only three expressions being evaluated. Second, we expect an error from the third expression, but the test fails when it happens.

Replace the lines:

data := []float64{22, 40, 0, 0}
for _, d := range data {
result, err := p.ProcessExpression(context.Background(), in)
if err != nil {

with:

data := []float64{22, 40, 0}
hasErr := []bool{false, false, true}
for i, d := range data {
result, err := p.ProcessExpression(context.Background(), in)
if err != nil && !hasErr[i] {

Jon Bodner
 
Mar 22, 2021  May 14, 2021
Chapter 13 Writing Tests
httptest

In:

server := httptest.NewServer(
http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
expression := req.URL.Query().Get("expression")
if expression != io.expression {
rw.WriteHeader(http.StatusBadRequest)
rw.Write([]byte("invalid expression: " + io.expression))
return
}
rw.WriteHeader(io.code)
rw.Write([]byte(io.body))
}))

Lines:
rw.WriteHeader(http.StatusBadRequest)
rw.Write([]byte("invalid expression: " + io.expression))
return

Should be:
t.Errorf("io `%s`, got `%s`", io.expression, expression)

Rationale:
"io.expression" is the expression the server expects to receive. "expression" is the actual expression received by the server. If the expected expression does not match the actual expression, then this is a test error, which should be reported. This is because it shows that RemoteSolver.Resolve() is unable to send the correct expression to the server.

Note from the Author or Editor:
This isn't really an error, but it's not clear. The code sample is showing how to use a mock http server, and the error text returned by the mock server isn't important, as long as it doesn't match the expected error message (or no error at all).

However, it would be better if the mock server returned an error that made clear exactly what went wrong. The best fix is to replace the line:

rw.Write([]byte("invalid expression: " + io.expression))

with:

fmt.Fprintf(rw, "expected expression '%s', got '%s'",
io.expression, expression)

The change is on page 291.

Kurt Yu  Mar 21, 2021  May 14, 2021
Chapter 10 Concurrency in Go
Backpressure

"The select tries to read a token from the channel. If it can, the function runs, and the token is returned."

Comment: The token is not returned. Instead a nil is returned:

func (pg *PressureGauge) Process(f func()) error {
select {
case <-pg.ch:
f()
pg.ch <- struct{}{}
return nil
default:
return errors.New("no more capacity")
}
}

Note from the Author or Editor:
This is not really an error, but the language can be made clearer. On page 218, the sentence:

"If it can, the function runs, and the token is returned."

should be changed to:

"If it can, the function runs, and the token is returned to the buffered channel."

Kurt Yu  Mar 10, 2021  May 14, 2021
Chapter 10 Concurrency in Go
When to Use Buffered and Unbuffered Channels

In:
func processChannel(ch chan int) []int {
const conc = 10
results := make(chan int, conc)
for i := 0; i < conc; i++ {
go func() {
results <- process(v)
}()
}
var out []int
for i := 0; i < conc; i++ {
out = append(out, <-results)
}
return out
}

Line:
results <- process(v)

Should be:
results <- process(i)

Also, function parameter ch is unused.

Note from the Author or Editor:
There is a line missing from the example.

On page 217, there should be a line:

v := <- ch

before the line:

results <- process(v)

The new line should be indented to match the existing line.

Kurt Yu  Mar 10, 2021  May 14, 2021
Chapter 10 Concurrency in Go
Using a Cancel Function to Terminate a Goroutine

In:
func countTo(max int) (<-chan int, func()) {
ch := make(chan int)
done := make(chan struct{})
cancel := func() {
close(done)
}
go func() {
for i := 0; i < max; i++ {
select {
case <-done:
return
default:
ch <- i
}
}
close(ch)
}()
return ch, cancel
}

Lines:
default:
ch <- i

Should be:
case ch <- i:

Rationale:
In using default, if writing to the ch channel blocks indefinitely, then the anonymous function blocks indefinitely too, even if the done channel is closed while the ch channel is blocked.

Note from the Author or Editor:
This is a mistake.

On page 216 the lines:
default:
ch <- i

Should be replaced with the single line:
case ch<-i:

The indentation for the replacement line should match up with the case <- done: above it.

Kurt Yu  Mar 10, 2021  May 14, 2021
chapter 8 Errors
Is and As

In:

func (me MyErr) Is(target error) bool {
if me2, ok := target.(MyErr); ok {
return reflect.DeepEqual(me,m2)
}
return false
}

The line:

return reflect.DeepEqual(me,m2)

should be:

return reflect.DeepEqual(me,me2)

Note from the Author or Editor:
This is correct.

On page 171, the line:

return reflect.DeepEqual(me,m2)

should be:

return reflect.DeepEqual(me, me2)

Kurt Yu  Mar 09, 2021  May 14, 2021
chapter 7 types, methods, and interfaces
Implicit Interfaces Make Dependency Injection Easier

in:

func main() {
l := LoggerAdapter(LogOutput)
ds := NewSimpleDataStore()
logic := NewSimpleLogic(l, ds)
c := NewController(l, logic)
http.HandleFunc("/hello", c.SayHello)
http.ListenAndServe(":8080", nil)
}


http.HandleFunc("/hello", c.SayHello)

should be

http.HandleFunc("/hello", c.logic.SayHello)

Note from the Author or Editor:
There is a mistake, but this isn't the fix.

On page 158, the line:

func (c Controller) HandleGreeting(w http.ResponseWriter, r *http.Request) {

should read:

func (c Controller) SayHello(w http.ResponseWriter, r *http.Request) {

Kurt Yu  Mar 09, 2021  May 14, 2021
Other Digital Version
67
Example 5-1. Using a struct to simulate named parameters

In 'Simulating Named and Optional Parameters', in the code of example 5-1, the second invocation of the function has an extra space between 'My' and 'Func' i.e "My Func(MyFuncOpts {" should be "MyFunc(MyFuncOpts {"

Note from the Author or Editor:
Thanks for catching this! I'll make sure that it's fixed as soon as possible.

The code sample is:

func main() {
MyFunc(MyFuncOpts {
LastName: "Patel",
Age: 50, })
My Func(MyFuncOpts {
FirstName: "Joe",
LastName: "Smith",
}) }

The second appearance of MyFunc has a space between My and Func. That needs to be removed.

James JJ  Feb 21, 2021  Mar 02, 2021
1
5. Writing Tests

> Note that this doesn’t exist all tests;

Did you mean "exits"?

Note from the Author or Editor:
Found and fixed in most recent revision.

Alexander  Oct 04, 2020  Mar 02, 2021
1
4. The Context

"ch := make(wrapper, 1)"

should be

"ch := make(chan wrapper, 1)"

Note from the Author or Editor:
Found and fixed in latest revision

Alexander  Oct 04, 2020  Mar 02, 2021
I
Copywrite frontmatter

Develepmental Editor: Michele Cronin

Note from the Author or Editor:
Typo in copyright.html. Fixed in latest git push.

Dave Cheney  Jul 09, 2020  Mar 02, 2021