2. Language Basics
Core syntax and primitives: allocation, defer, constants, declarations, and control flow.
Q1 What is the difference between new(T) and make(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
Q2 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
}
Q3 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.
Q4 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
)
Q5 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.
Q6 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.
Q7 What is panic and recover 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.
Q8 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.
Q9 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.