-
T/F? In Go, semicolons are required to terminate statements.
Answer
True, but it's usually the compiler that inserts them on behalf of the programmer. Go Spec: Semicolons -
T/F? You can define lexically scoped blocks anywhere in a function.
Answer
True: Go Spec: Blocks (which means you can do stuff like this.
-
T/F? A deferred function's arguments are not evaluated until the deferred statement is executed.
Answer
False: The Go Blog: Defer, Panic, and Recover
A deferred function's arguments are evaluated when the defer statement is evaluated.
-
T/F? In a panicking goroutine
recover()
will return whatpanic()
received & resume execution.Answer
True: The Go Blog: Defer, Panic, and Recover
Recover is a built-in function that regains control of a panicking goroutine...During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.
-
T/F? The recover function is only useful inside deferred functions.
Answer
True: The Go Blog: Defer, Panic, and Recover
Recover is only useful inside deferred functions.
-
T/F? Deferred function calls are executed in First-In-First-Out order.
Answer
False: The Go Blog: Defer, Panic, and Recover
Deferred function calls are executed in Last In First Out order after the surrounding function returns.
-
T/F? Deferred functions may read but not assign to the returning function's named return values.
Answer
False: The Go Blog: Defer, Panic, and Recover
Deferred functions may read and assign to the returning function’s named return values.
-
T/F? Excluding cases such as panics, a go program exits when all goroutines have finished.
Answer
False: The Go Spec: Program Execution
Program execution begins by initializing the program and then invoking the function main in package main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.
-
T/F? Naked returns (like naked mole rats) are visually unappealing, best left out of sight.
Answer
TRUE
-
Given
type Point struct { X, Y int }
why would this code make any self-respecting gopher cringe?p := Point{1, 2}
Answer
The assignment statement utilizes a composite literal (see the Go Spec: Composite Literals. When a struct type is initialized using a composite literal:
An element list that does not contain any keys must list an element for each struct field in the order in which the fields are declared.
The above is a very rigid requirement. Always specifying keys (field names) in struct literals is a much more flexible way to code and allows for the addition and re-ordering of fields without breaking any calling code.
-
Complete the following two Go Proverbs: 'Clear is better than ______. __________ is never clear.'
Answer
Clear is better than clever. Reflection is never clear.
-
Jeopardy Clue: "The utility whose style is no one's favorite, but is still everyone's favorite."
Answer
What is
go fmt
? (see the Go Proverbs) -
What is the difference between concurrency and parallelism, and why does it matter?
Answer
Concurrent execution refers to the capability of the runtime to multi-task when presented with, for instance, blocking I/O. Parallelism refers to executing multiple tasks at the same time (using multiple processors/threads). A concurrent program written in Go can be executed in parallel simply by ensuring that
GOMAXPROCS
is greater than1
(the default value since Go 1.5 has been the number of cores available). -
The
strings.NewReplacer()
function panics if an odd number of arguments are passed. Is this a good approach?Answer
It certainly seems odd for a standard library function such as this to panic if passed an odd number of arguments. Perhaps it would be better to have defined the function with an error return value:
def NewReplacer(args ...string) (*Replacer, error) {...}
Because of the Go 1.0 compatibility promise, the signature for this function won't be changing anytime soon, but we could create a custom 'builder' to smooth this out, something with a
Register(search, replace string)
method. -
What fmt 'verb' produces the same output as
fmt.Sprintf("%s", reflect.TypeOf(a))
but without any call toreflect.TypeOf()
?Answer
fmt.Sprintf("%T", a)
-
T/F? Spaces are always added between operands passed to fmt.Println().
Answer
True
-
T/F? Spaces are always added between operands passed to fmt.Print().
Answer
False (only when neither is a string)
-
Simplify by removing a redundant element:
csv.NewReader(bufio.NewReader(file))
Answer
Since the csv.NewReader function wraps the provided reader in bufio.NewReader for the caller, it's sufficient to simply use
csv.NewReader(file)
. -
Simplify by replacing with another 'strings' function:
strings.Split(s, "\t\n\v\f\r ")
Answer
strings.Fields(s)
-
Simplify by replacing with another 'strings' function:
strings.Replace(s, "a", "b", -1)
Answer
strings.ReplaceAll(s, "a", "b")
-
T/F?
a := new(Thing)
is equivalent toa := Thing{}
Answer
False! The
new
function returns a pointer. -
T/F?
var a any = new(Thing)
is equivalent tovar a any = reflect.New(reflect.TypeOf(Thing{})).Interface()
Answer
True! The
reflect.New
function is analogous to the built-innew
function. They both return pointers to a newly allocated zero-value of the provided type. -
T/F?
t1 := T{}; t2 := reflect.ValueOf(t1).Interface(); fmt.Print(t1 == t2)
Answer
True! See "The Laws of Reflection".
-
How many bits are in every
int
?Answer
The answer is dependent upon processor architecture. Most likely your int values have 64 bits, unless the architecture of your processor is 32-bit.
-
In Go,
while
is spelled___
.Answer
There are 3 forms of the
for
loop in Go. The one called "For statements with single condition" operates the same aswhile
loops common in other languages. -
T/F? After being compiled/parsed, regular expression patterns should only be used once.
Answer
False! The point of compiling a regular expression is so you can then reuse it many times. In my own benchmarks, compiling a simple regex takes ~800ns while matching against a string with the compiled regex only takes ~80ns (that's a 10x difference). So, for simple regular expressions and small strings, it's very advantageous to pre-compile the regular expression pattern once and then reference it as needed later. Moral of the story: Don't call
regexp.Compile(...)
every time you want to perform a match.Try writing your own benchmarks to see how the complexity of a regular expression pattern and the length of the text against which you match affect runtime. (The results may surprise you!)
-
What does
io.EOF
stand for and in what scenarios might that be something of a misnomer?Answer
End of file. It is a slight misnomer in the context of any
io.Reader
whose underlying source is NOT an instance of*os.File
. Perhaps this is why, in the officialio
package documentation, the acronym is never explicitly decoded. -
T/F?
any
is an alias forinterface{}
and is equivalent tointerface{}
in all ways.Answer
-
T/F?
make(map[k]v)
is the same asmap[k]v{}
Answer
-
T/F? Attempting to retrieve a map value will panic if the key is not present.
Answer
False! The zero value will be returned (without any panic).
-
T/F? Attempting to retrieve a value from a nil map will cause a panic.
Answer
False! The zero value will be returned.
-
T/F? Maps are safe for concurrent use across any number of goroutines.
Answer
False! Concurrent map access must be protected by a mutex. Alternatively, you could consider using sync.Map.
-
T/F? Struct types may be used as map keys.
Answer
True! This is a handy way to index values by a combination of key properties.
-
T/F? When iterating over a map with a range loop, the iteration order is not specified.
Answer
True! It is also not guaranteed to be the same from one iteration to the next.
-
A sync.WaitGroup can be compared to a thread-safe (A) Queue, (B) Counter, (C) Stack, or (D) Mutex.
Answer
B. The sync.WaitGroup is effectively a fancy (i.e. 'thread-safe') Counter with some pretty specific rules about how it should be used.
-
T/F? Calling sync.WaitGroup.Add() is ok at any point during the life cycle of the WaitGroup.
Answer
False! Add should be called from the 'main' goroutine and should not be called while a call to Wait() is blocked.
-
T/F? The sync.WaitGroup.Done method can be called many times from each awaited goroutine.
Answer
True, but only if you like code that panics. In practice, Done should only be called once from each awaited goroutine, usually as a deferred call.
-
T/F? The sync.WaitGroup.Wait method blocks until the counter is zero or up to 10 minutes.
Answer
False, there is no timeout. It blocks until all awaited goroutines finish. End of story.
-
T/F? A sync.WaitGoup should only be reused once all Wait calls unblock.
Answer
True! Don't get fancy with a WaitGroup. Use it to wait for a batch of goroutines. Only after Wait has unblocked may you use the same instance to wait for another batch of goroutines.
-
T/F? It is possible to recover from a panicking goroutine in the goroutine that launched it.
Answer
False! You must call
recover
somewhere higher in the call stack of panicking goroutine, otherwise the program will crash. -
T/F? A Git repository may contain a single Go module definition.
Answer
False! A single Git repository may contain multiple Go module definitions.
-
T/F? A module version number like 'v0.*.*' makes no stability or backward compatibility guarantees.
Answer
True! It is only once a module graduates to 'v1.*.*' that module developers must be conscious of backward compatibility.
-
T/F? To publish a breaking change to a 'v1' module, simply tag with 'v2.0.0' and push it.
Answer
False! Once a module reaches 'v2.*.*' it must contain '/v2' in its module declaration and package import paths. Modules at 'v0.*.*' or 'v1.*.*' are not subject to any such requirement.
-
Fill in the Go proverb: "A little _______ is better than a little dependency."
Answer
"copying"
-
Fill in the Go proverb: "Errors are ______."
Answer
"values"
-
Fill in the Go proverb: "The bigger the interface, the ______ the abstraction."
Answer
"weaker"
-
T/F? The following is an untyped string const:
const a = "b"
Answer
True! See Constants on the Go blog.
-
T/F? generic funcs work with arguments of various types based on type parameters and constraints.
Answer
True! Generics in Go (aka "type parameters") enable writing reusable code that can work with various data types. Common example: a function that can operate on a slice of any item type.
-
T/F? Go's implementation of generics provides dynamic typing, i.e. types are determined at runtime.
Answer
False. Go's generics are statically typed, meaning type information is determined at compile time.
-
T/F? Generics in Go can only be used with built-in types like slices and maps.
Answer
False. Generics in Go can be used with user-defined types as well, providing flexibility in creating generic algorithms and data structures.
-
T/F? Go's generics feature was introduced in Go 1.18 as an experimental feature.
Answer
False. Go 1.18 introduced generics as an official feature, but a few packages were released as 'experimental' on the golang.org/x/exp subrepository (constraints/slices/maps) to allow developers to use and provide feedback on real-world scenarios. Much of the code defined in those experimental packages has been incorporated into the standard library since then.
-
This question can be found at the Go Playground, but here's the relevant snippet of code:
Reveal code snippet
date_2023_01_30 := time.Date(2023, time.January, 30, 0, 0, 0, 0, time.UTC) one_month_later := date_2023_01_30.AddDate(0, 1, 0) date_2023_02_28 := time.Date(2023, time.February, 28, 0, 0, 0, 0, time.UTC) fmt.Println("T/F?", one_month_later == date_2023_02_28)
Answer
False! The
one_month_later
value is equal to"2023-03-02"
. From the docs:AddDate
normalizes its result in the same way thatDate
does, so, for example, adding one month to October 31 yields December 1, the normalized form for November 31. -
T/F?
make(chan int)
is equivalent tomake(chan int, 1)
.Answer
False!
make(chan int)
is equivalent tomake(chan int, 0)
. -
T/F? A send to a nil channel panics.
Answer
False! A send to a nil channel blocks forever. See Channel Axioms, by Dave Cheney
-
T/F? A receive from a nil channel panics.
Answer
False! A receive from a nil channel blocks forever. See Channel Axioms, by Dave Cheney
-
T/F? A send to a closed channel panics.
Answer
True! See Channel Axioms, by Dave Cheney
-
T/F? A receive from a closed channel panics.
Answer
False! A receive from a closed channel returns the zero value immediately. See Channel Axioms, by Dave Cheney
-
T/F? A closed channel never blocks.
Answer
True! See Curious Channels, by Dave Cheney
-
T/F? A nil channel never blocks.
Answer
False! A nil channel always blocks. See Curious Channels, by Dave Cheney
-
T/F? Calling a defined method on a nil-pointer panics immediately.
Answer
False! Unlike other languages that are more object-oriented, a Go method is essentially just a function which receives the 'receiver' as a regular (though somewhat obscured) argument. It is only when a field on the nil receiver is accessed that a panic will occur.
-
T/F? Calling
*testing.T.Fail()
ends the currently executing test immediately.Answer
False. The Fail method only marks the test as failed. The test will continue execution.
-
T/F? Calling
*testing.T.Fatal()
panics immediately.Answer
False. The Fatal method ends a test immediately, but without panicking.
-
Simplify:
log.Println(fmt.Sprintf("Hello, %s.", name))
Answer
log.Printf("Hello, %s.", name)