2. Language Basics
Core syntax and primitives: allocation, defer, constants, declarations, and control flow.
Question: What is the difference between
new(T)
andmake(T)
in Go?
Answer: new(T)
allocates memory for a new value of type T
, zeroes it, and returns a pointer to it (*T
). make(T, ...)
is used only for creating and initializing slices, maps, and channels; it returns a ready-to-use value of type T
(not a pointer).
Explanation: new
allocates memory for any type. make
is a special built-in for the three built-in reference-like types that need to be initialized before use. For example, make([]int, 0, 10)
creates a slice descriptor with a pointer to an underlying array, a length of 0, and a capacity of 10. Using new([]int)
would just return a pointer to a nil
slice descriptor.
For maps specifically, a nil
map cannot accept key assignments and will panic. Always create maps with make
(or make
+ capacity
) before writing:
var m map[string]int // nil map; reads are ok, writes panic
m = make(map[string]int)
m["k"] = 1 // safe
Question: What is the
defer
keyword in Go and what is a common use case?
Answer: The defer
statement schedules a function call to be run immediately before the surrounding function returns. A common use case is to guarantee that resources are cleaned up, such as closing a file or unlocking a mutex.
Explanation: Deferred functions are executed in Last-In, First-Out (LIFO) order. This makes resource management clean and less error-prone. You can open a file and immediately defer f.Close()
on the next line, ensuring the file is closed regardless of how the function exits (e.g., a normal return, a panic, or multiple return points).
func processFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // Guaranteed to run before the function returns
// ... do work with f ...
return nil
}
Question: When are
defer
arguments evaluated, and can deferred functions change return values?
Answer: Deferred function arguments are evaluated immediately at the point of defer
. Deferred functions run after the return value is set; with named returns, they can modify the result.
Explanation: Capture values you need at defer time. To use the latest state, pass pointers or reference outer variables intentionally.
Question: How do constants and
iota
work?
Answer: const
defines compile-time constants. iota
auto-increments per const block, useful for enums and bitmasks.
Explanation: iota
resets to 0 for each const
block and increments by one per line.
type State int
const (
Unknown State = iota
Ready
Running
)
const (
FlagA = 1 << iota
FlagB
FlagC
)
Question: What are rules for short declarations (
:=
) and variable shadowing?
Answer: :=
requires at least one new variable on the left side in the current scope. Be careful not to shadow err
or other vars in inner scopes.
Explanation: Shadowing can hide outer variables and cause subtle bugs (e.g., if x, err := f(); err != nil { ... }
where x
is scoped to the if
). Prefer explicit var
or reuse existing names intentionally.
Question: What is the blank identifier
_
used for?
Answer: To explicitly ignore a value (including imports for side-effects) and to satisfy interface requirements during assignments/tests.
Explanation: Use _ = value
to avoid unused variable errors when needed.
Question: What is
panic
andrecover
in Go, and when should they be used?
Answer: panic
is a built-in function that stops the ordinary flow of control and begins panicking. recover
is a built-in function that regains control of a panicking goroutine. They should be used for truly exceptional, unrecoverable situations within a program, not for normal error handling.
Explanation: Go's philosophy is to use explicit error
return values for expected errors (e.g., file not found, network failure). panic
is reserved for catastrophic failures that indicate a programmer error (e.g., index out of bounds, nil pointer dereference). A common pattern is for a library to panic
internally and then recover
at its public API boundary, converting the panic into a returned error
value.
Question: How does
switch
work in Go, and does it fall through by default?
Answer: switch
picks the first matching case and does not fall through by default. Use fallthrough
explicitly to proceed to the next case.
Explanation: Cases can be expressions; switch
can omit the tag (switch {}
) for boolean conditions. Prefer clear, independent cases over fallthrough
chains.
Question: What are raw string literals and when should you use them?
Answer: Raw string literals use backticks (`...`
) and do not interpret escapes; they’re great for multi-line text or regexes. Interpreted string literals use quotes and process escapes like \n
.
Explanation: Raw strings avoid double-escaping and simplify embedding multi-line content.