Sharpen your understanding of advanced Go programming with this quiz focused on real-world scenarios, concurrency, interfaces, memory management, and Go-specific patterns. Ideal for preparing for interviews or deepening your expertise in Go language principles and practical use.
In Go, what is the main purpose of the Go scheduler when running concurrent goroutines?
Explanation: The Go scheduler's primary responsibility is to manage the allocation of CPU time among multiple concurrent goroutines, allowing efficient multitasking. It does not directly prevent deadlocks, which must be handled by careful coding. Increasing heap memory size is managed by the garbage collector and runtime, not the scheduler. Determining package dependencies is handled during build time, unrelated to goroutine scheduling.
Given the code 'var i interface{} = 42', what happens if you use 's := i.(string)' in Go?
Explanation: A type assertion like 'i.(string)' causes a runtime panic if the underlying type of i does not match the asserted type; in this case, it holds an int, not a string. There is no compile-time error because interfaces allow dynamic types. The value won't be set to an empty string, and Go does not silently return nil for failed assertions. Safe assertions can use the ', ok' idiom to avoid panics.
If a Go program creates an unbuffered channel and sends a value without a goroutine ready to receive, what will happen?
Explanation: Sending on an unbuffered channel blocks until another goroutine is ready to receive, so if none is ready, the program will deadlock. The value is not lost; it's waiting to be received. The error 'assignment to nil channel' is unrelated and only occurs if you use a nil channel. The send won't complete immediately because unbuffered channels require synchronization.
What happens to an underlying array when a slice in Go grows beyond its capacity during append operations?
Explanation: When a slice exceeds its current capacity, Go allocates a new, larger array and copies the elements, then the slice reference is updated to point to the new array. The original array cannot be extended because arrays are fixed-size in Go. Append does not fail with an out-of-bounds error; it handles resizing internally. Slice elements are not truncated during this process.
What is a key benefit of using a pointer receiver instead of a value receiver for methods in Go?
Explanation: A pointer receiver allows the method to modify the actual value the receiver points to, enabling changes to persist outside the method scope. It does not directly affect package import performance. Memory usage may, in fact, be slightly higher due to pointers, and pointer receivers do not influence circular dependencies, which are resolved by careful package structuring.
What is the default zero value of a Go map after declaration like 'var m map[string]int'?
Explanation: Declaring a map with 'var' but without initialization results in a nil map in Go. It is not automatically an empty map; that would require explicit initialization. There won't be a map with a zero-valued entry unless you assign something. 'False' is the zero value for booleans, not maps.
Which concurrency primitive allows safe sharing of state between goroutines in Go?
Explanation: Channels are designed for safe communication and synchronization between goroutines in Go. Pointers, arrays, and slices themselves do not provide safety and can lead to race conditions if used across goroutines without proper synchronization. Channels encapsulate data transfer and avoid many common concurrency bugs.
What is the order of execution for multiple deferred function calls in a single Go function?
Explanation: Defer statements in Go execute in a last-in-first-out (LIFO) order after the surrounding function returns. They are not executed concurrently or in a first-in-first-out order. Each defer is stacked, so the most recently deferred function runs first on exit, not randomly or all at once.
What does escape analysis help determine in Go during compilation?
Explanation: Escape analysis helps the Go compiler decide if a variable can stay on the stack or needs to be moved to the heap for runtime access. It does not determine function ownership, check syntax, or assess garbage collector necessity directly. Instead, it impacts memory performance optimizations.
When are 'init()' functions executed in each Go package during program startup?
Explanation: init() functions in a package are called after the init functions of all its imported dependencies, ensuring proper setup order. They do not run before package-level variable declarations—those are evaluated first. init() executes just before main starts running but after package initialization, and it is only called once per import per package.