Functions in Go
Functions are first-class citizens in Go — they can be assigned to variables, passed as arguments, and returned from other functions.
1. Syntax Anatomy
// ↓ keyword ↓ name ↓ parameters ↓ return type
func divide (a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("divide by zero")
}
return a / b, nil
}- Parameters of the same type can share the type:
a, b int - Multiple return values are wrapped in
() erroris always the last return value by convention
2. Return Styles
// Single return
func square(x int) int {
return x * x
}
// Multiple returns — idiomatic Go
func divide(a, b int) (int, error) {
if b == 0 { return 0, fmt.Errorf("divide by zero") }
return a / b, nil
}
// Named returns — use sparingly, good for short functions
func minMax(nums []int) (min, max int) {
min, max = nums[0], nums[0]
for _, n := range nums[1:] {
if n < min { min = n }
if n > max { max = n }
}
return // naked return — returns min and max
}⚠️ Named returns + naked
returnhurt readability in long functions. Use only when it clearly adds documentation value.
3. First-Class Functions
// Assign to variable
greet := func(name string) string {
return "Hello, " + name
}
fmt.Println(greet("Go")) // Hello, Go
// Pass as argument (higher-order function)
func apply(x int, fn func(int) int) int {
return fn(x)
}
fmt.Println(apply(5, func(n int) int { return n * 2 })) // 10
// Return a function (factory / closure)
func multiplier(factor int) func(int) int {
return func(x int) int {
return x * factor
}
}
double := multiplier(2)
fmt.Println(double(7)) // 144. Closures ⭐
A closure is a function that captures variables from its surrounding scope. The captured variable is shared — not copied.
func counter() func() int {
count := 0
return func() int {
count++ // count lives on the heap — shared with the closure
return count
}
}
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
c2 := counter() // independent counter
fmt.Println(c2()) // 1Production use: closures are used for middleware, callbacks, memoization, and functional options patterns.
5. Anonymous Functions & IIFE
// Anonymous function assigned to a variable
square := func(x int) int { return x * x }
fmt.Println(square(4)) // 16
// IIFE — declared and called immediately
result := func(a, b int) int {
return a + b
}(10, 5)
fmt.Println(result) // 15
// Common use: goroutine IIFE
go func(msg string) {
fmt.Println(msg)
}("hello from goroutine")6. defer in Functions
defer schedules a function call to run when the surrounding function returns — regardless of how it returns (normal, error, panic).
func readFile(path string) error {
f, err := os.Open(path)
if err != nil { return err }
defer f.Close() // ✅ guaranteed to run even if function panics
// ... read file
return nil
}Defer rules:
- Arguments are evaluated immediately, not at run time
- Multiple defers run in LIFO order
- Deferred functions can read/modify named return values
// Defers run LIFO
defer fmt.Println("third")
defer fmt.Println("second")
defer fmt.Println("first")
// Output: first → second → third7. init() Function
func init() {
// runs automatically before main()
// no parameters, no return value
}| Property | Detail |
|---|---|
| Runs before | main() |
| Parameters / returns | None |
| Per file | Multiple init() allowed |
| Order | File order within package; import dependency order across packages |
| Use for | Registering drivers, loading config, validating flags |
| Avoid | Business logic, side effects that are hard to test |
var db *sql.DB
func init() {
var err error
db, err = sql.Open("postgres", os.Getenv("DB_URL"))
if err != nil {
log.Fatal(err)
}
}8. Recursion
func factorial(n int) int {
if n == 0 { return 1 }
return n * factorial(n-1)
}
// With memoization (production pattern)
var memo = map[int]int{}
func fib(n int) int {
if n <= 1 { return n }
if v, ok := memo[n]; ok { return v }
memo[n] = fib(n-1) + fib(n-2)
return memo[n]
}9. Function Types & Type Aliases
// Define a function type
type Transformer func(int) int
func applyAll(nums []int, fns ...Transformer) []int {
result := make([]int, len(nums))
copy(result, nums)
for _, fn := range fns {
for i, v := range result {
result[i] = fn(v)
}
}
return result
}
double := Transformer(func(x int) int { return x * 2 })
addOne := Transformer(func(x int) int { return x + 1 })
fmt.Println(applyAll([]int{1, 2, 3}, double, addOne)) // [3 5 7]10. Common Mistakes ⚠️
| Mistake | Problem | Fix |
|---|---|---|
| Ignoring error return | silent failures | always check err != nil |
Naked return in long function | hard to read | use explicit returns |
| Closure capturing loop var (pre-1.22) | all closures see same value | shadow: v := v or use Go 1.22+ |
defer argument captured late | wrong value deferred | args evaluated at defer statement, not at call |
Calling init() manually | undefined behaviour | init is called by runtime only |
| Huge function doing everything | untestable | break into small, single-purpose functions |
11. Interview Cheat Sheet
Q: What makes Go functions first-class?
They can be assigned to variables, passed as arguments, and returned from other functions — just like any other value.
Q: What is a closure?
A function that captures and shares variables from its enclosing scope. The captured variable lives on the heap and is shared between the closure and the outer scope.
Q: When are defer arguments evaluated?
Immediately when the
deferstatement is reached — not when the deferred function actually executes. Only the function call is delayed.
Q: What is the difference between anonymous function and closure?
All closures are anonymous functions, but not all anonymous functions are closures. A closure specifically captures at least one variable from the outer scope.
Q: What is init() and when does it run?
A special no-arg, no-return function that runs automatically before
main(). Multipleinit()functions per file/package are allowed. Used for setup like registering drivers or validating env vars.
Q: Should you use named return values?
Only for short functions where they act as documentation. In long functions, naked
returnstatements make code hard to follow and should be avoided.