Basics & Packages
How a Go program is structured — packages and imports, variable and constant declarations, basic types, zero values, iota, identifier visibility, and the build tools.
15 questions
JuniorTheoryVery commonWhat are the zero values of Go's basic types?
What are the zero values of Go's basic types?
Every variable declared without an initializer gets its type's zero value, so Go has no uninitialised garbage. Numbers are 0, bool is false, and string is "" (empty, not nil). Pointers, slices, maps, channels, functions, and interfaces are nil. A struct's zero value has all fields zeroed, and an array's elements are each zeroed too.
Common mistakes
- ✗Thinking an uninitialised variable holds garbage — Go always zeroes it
- ✗Believing a zero-value
stringis nil rather than the empty string"" - ✗Forgetting that slices, maps, channels, and interfaces zero to
nil
Follow-up questions
- →What happens when you write to a nil map versus read from one?
- →Why can a nil slice still be appended to safely?
JuniorTheoryCommonWhat are Go's basic types, and what rules govern converting between them?
What are Go's basic types, and what rules govern converting between them?
Go's basic types are fixed-width integers (int, int8..int64, the uint family), floats (float32, float64), bool, and string, with byte an alias for uint8 and rune for int32. There is no implicit numeric conversion: you must convert explicitly, like float64(x), even between an int and an int64.
Common mistakes
- ✗Expecting an
intto widen into afloat64automatically without a cast - ✗Thinking
byteandruneare distinct types rather than aliases foruint8/int32 - ✗Assuming an
intand anint64are the same type and need no conversion
Follow-up questions
- →What can go wrong when you convert a
float64to anintin Go? - →How does the size of
intdepend on the target platform?
JuniorTheoryCommonWhat do go run, go build, and go mod each do in Go?
What do go run, go build, and go mod each do in Go?
go run compiles the program to a temporary location and runs it immediately, leaving no binary behind. go build compiles the package into a single self-contained executable on disk without running it. go mod manages the module and its dependencies, with subcommands like go mod init and go mod tidy.
Common mistakes
- ✗Thinking
go runleaves a binary on disk likego builddoes - ✗Believing a
go buildexecutable needs the Go runtime installed to run - ✗Confusing
go mod(dependency management) with a plain package downloader
Follow-up questions
- →What is the difference between
go mod initandgo mod tidy? - →How would you cross-compile a
go buildbinary for another OS or architecture?
JuniorTheoryCommonWhat happens when this program is built and run?
What happens when this program is built and run?
It does not compile
Open full question →Common mistakes
- ✗Expecting
3:constdeclares a value fixed at compile time, solimit = nis refused; only avaror:=variable takes a new value. - ✗Expecting
10or a run-time error: the assignment is not skipped at run time, the compiler rejects it withcannot assign to limit.
Follow-up questions
- →What is the difference between a typed and an untyped constant in Go?
- →When does
:=re-use an existing variable versus declare a new one?
JuniorTheoryCommonWhat is the import path of the package in directory store/ of module example.com/shop?
What is the import path of the package in directory store/ of module example.com/shop?
example.com/shop/store
Common mistakes
- ✗Expecting
store: an import path is the module path from go.mod plus the directory, and a barestoreis looked up in the standard library. - ✗Expecting
example.com/store: the directory is appended to the whole module path,example.com/shop, not to its domain.
Follow-up questions
- →How is the name in the
packageclause related to the name of its directory? - →Why do module paths usually look like a repository address such as
github.com/user/repo?
JuniorTheoryCommonWhat does the program print?
What does the program print?
0 2
Open full question →Common mistakes
- ✗Expecting
0 1: the_line is still a line of the block, soiotais1there and the value is thrown away;Cgets2. - ✗Expecting
0 0: a line with no=repeats the expressioniota, not the value0, andiotagrows by one on every line of the block.
Follow-up questions
- →How would you make the first constant of the block start at
1? - →What does
iotaequal in the first line of a second, separateconstblock?
JuniorTheoryCommonWhat happens when this program is built and run?
What happens when this program is built and run?
It does not compile
Open full question →Common mistakes
- ✗Expecting
4.5: Go never converts types implicitly, soa(anint) andb(afloat64) cannot be added until one is converted, as infloat64(a) + b. - ✗Expecting
4or a run-time error: the compiler rejects the mismatch withmismatched types int and float64, so no program is built.
Follow-up questions
- →How would you change the program so that it prints
4.5? - →Why does
fmt.Println(3 + 1.5)compile whena + bdoes not?
JuniorTheoryCommonThe code no longer imports anything from github.com/acme/log; what does go mod tidy do to go.mod?
The code no longer imports anything from github.com/acme/log; what does go mod tidy do to go.mod?
Removes the require line for github.com/acme/log
Common mistakes
- ✗Expecting only go.sum to change: go.mod and go.sum follow the imports together, so the
requireline goes as well. - ✗Expecting
requirelines to be edited only by hand:go mod tidyadds the modules the imports need and removes the ones they no longer use.
Follow-up questions
- →What does the
// indirectcomment after arequireline mean? - →What does go.sum store, and should it be committed with go.mod?
JuniorTheoryCommonWhat happens when this program is built and run?
What happens when this program is built and run?
It does not compile
Open full question →Common mistakes
- ✗Expecting
GO go:importopens only exported names, those starting with a capital letter, sostrings.toLoweris undefined. - ✗Expecting a run-time error or a skipped call: the compiler checks every name first and stops with
undefined: strings.toLower.
Follow-up questions
- →How does an import path differ from the package name used to call it?
- →What does a blank import
import _ "pkg"do, and when is it used?
JuniorTheoryCommonWhat happens when this program is built and run?
What happens when this program is built and run?
It does not compile
Open full question →Common mistakes
- ✗Expecting
hi: Go has no warnings, and a local variable that is declared but never read stops the build withdeclared and not used: x. - ✗Expecting the problem to show up at run time: an unused variable is found by the compiler, so no program is built and nothing runs.
Follow-up questions
- →How would you change the program so that it builds without using
xanywhere? - →What does Go do with an imported package that the code never uses?
JuniorTheoryCommonHow does Go decide whether an identifier is exported from a package?
How does Go decide whether an identifier is exported from a package?
Visibility is decided purely by the first letter of the name. An identifier whose first letter is uppercase is exported and visible from other packages; a lowercase first letter keeps it package-private. The rule applies uniformly to functions, types, variables, constants, struct fields, and methods — there is no public/private keyword.
Common mistakes
- ✗Thinking Go has
public/privatekeywords instead of a capitalisation rule - ✗Believing struct fields are always visible regardless of their first letter
- ✗Assuming the rule applies only to functions, not to fields, methods, or consts
Follow-up questions
- →Can an exported struct have unexported fields, and what does that imply?
- →How do
internal/packages further restrict who may import a package?
JuniorTheoryCommonWhat does the program print?
What does the program print?
0 "" false
Open full question →Common mistakes
- ✗Expecting
<nil>for the string: the zero value ofstringis the empty string"", and%qprints it as""; astringcan never benil. - ✗Expecting a compile error for reading variables never assigned: every variable declared without a value starts at its type's zero value and can be read.
Follow-up questions
- →What would
fmt.Println(i, s, b)print for the same three variables? - →What is the zero value of a
float64and of a pointer?
JuniorCodeOccasionalWhat values do the constants get from iota in these two const blocks?
What values do the constants get from iota in these two const blocks?
A=0, B=1, D=3 — iota starts at 0 in each const block and increments by one per line, including the skipped _ line (which consumes value 2). In the second block KB = 1 << (10 * (iota + 1)) gives KB=1024 and MB=1048576, because iota is 0 then 1 on successive lines.
Common mistakes
- ✗Thinking a skipped
_line does not advanceiota— it still consumes a value - ✗Believing
iotais a global counter rather than resetting perconstblock - ✗Forgetting
iotais evaluated inside the full line expression, e.g.1 << (10*(iota+1))
Follow-up questions
- →How would you define
KB, MB, GBas power-of-two sizes using oneiotaexpression? - →What happens to
iotaif two constants share a single line viaA, B = iota, iota?
MiddleTheoryOccasionalHow do typed constants and iota build an enum in Go, and how do they differ from untyped constants?
How do typed constants and iota build an enum in Go, and how do they differ from untyped constants?
A const block with iota generates successive values per line. Typing the first const (type Weekday int; Sunday Weekday = iota) makes the whole column that typed enum. The gotcha: an untyped constant implicitly converts to whatever type the context needs, but a TYPED constant does not. So mixing a Weekday value with a plain int is a compile error — you need an explicit int(Sunday).
Common mistakes
- ✗Thinking a named enum type and a plain
intinterchange without an explicit conversion - ✗Forgetting
iotaresets perconstblock, not per line, and increments each line - ✗Confusing untyped-constant implicit conversion with typed-constant strictness
Follow-up questions
- →How do you give an enum readable names, e.g. via a
String()method? - →What makes an untyped constant's default type, and when does it apply?
MiddleTheoryRareWhy can := in an inner scope silently shadow an outer variable?
Why can := in an inner scope silently shadow an outer variable?
:= always declares NEW variables in the current block. If a name from an outer scope is on its left, an inner := makes a fresh inner variable that shadows — not reassigns — the outer one, so writes through it never reach the outer variable. The fresh shadow starts at its zero value: a classic err-handling bug. Use = to assign, or declare once and reuse.
Common mistakes
- ✗Assuming an inner
:=on an outer name reassigns it instead of declaring a shadow - ✗Using
:=inside anif/forblock and reading a stale outererrafterwards - ✗Believing the shadow inherits the outer value rather than starting at its zero value
Follow-up questions
- →Which Go tools or vet checks can flag an accidental shadow?
- →When does
:=legitimately reuse an outer name without shadowing it?