2. Language Basics

Core syntax and primitives: allocation, defer, constants, declarations, and control flow.

Question: 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

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 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.

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.