Functions
Overview
-
Declaring a function:
func add(a int, b int) int { return a + b } -
Calling a function:
result := add(2, 3) // result = 5
Extra:
-
Multiple return values:
func swap(a, b int) (int, int) { return b, a } x, y := 1, 2 x, y = swap(x, y) // x = 2, y = 1 -
Ignoring return values:
x, _ = getCoords() -
Named return values:
The function automatically returns the named variables.
func calculate(x int) (result int) { result = x * x return } -
Variadic functions:
Accept zero or more arguments of the same type, which are treated as a slice inside the function.
func sum(numbers ...int) int { total := 0 for _, number := range numbers { total += number } return total } sum(2, 5, 7, 10) // 24 -
First-class functions:
Functions can be assigned, passed, and returned.
func main() { price := 100.00 fmt.Printf("Regular user price: $%.2f\n", applyDiscount(price, regularDiscount)) // Regular user price: $90.00 fmt.Printf("VIP user price: $%.2f\n", applyDiscount(price, vipDiscount)) // VIP user price: $80.00 } func vipDiscount(price float64) float64 { return price * 0.8 } func regularDiscount(price float64) float64 { return price * 0.9 } func applyDiscount(price float64, strategy func(float64) float64) float64 { return strategy(price) } -
Anonymous functions:
Anonymous functions are functions without a name.
func main() { func() { fmt.Println("Hello, world!") }() // immediately invoking the anonymous function }Assigning an anonymous function to a variable:
myVar := func(a, b int) { fmt.Println(a + b) } myVar(1, 2) // 3
Pass-by Value & Reference
All arguments in Go are passed by value. The difference is what gets copied.
-
Pass-by Value:
Pure value types copy the actual data. The function works with its own copy. Changes made inside the function stay local and do not affect the original.
func increment(num int) { num++ fmt.Println("Inside:", num) // Inside: 6 } func main() { num := 5 increment(num) fmt.Println("Outside:", num) // Outside: 5 }Pure Value Types (
pass-by-value): Booleans, Numerics, Arrays, Structs.a := [3]int{2, 4, 6} // array b := a b[1] = 99 fmt.Println(a, b) // [2 4 6] [2 99 6] -
Pass-by Reference:
Reference-containing types copy a value that references shared underlying data. Changes to that data are visible outside.
func change(s []int) { s[0] = 99 } func main() { nums := []int{1, 2, 3} fmt.Println(nums) // [1 2 3] change(nums) fmt.Println(nums) // [99 2 3] }Reference-Containing Types (
pass-by-reference): Strings (immutable), Slices, Maps, Channels, Pointers (&x).a := []int{2, 4, 6} // slice b := a b[1] = 99 fmt.Println(a, b) // [2 99 6] [2 99 6]Even though a value may allow modification of shared underlying data, the variable itself is still passed by value. Reassigning the parameter inside the function changes only the local copy and does not affect the original variable in the caller.
func reassignSlice(s []int) { s = []int{100, 200, 300} s[0] = 99 } func main() { nums := []int{1, 2, 3} fmt.Println("Before:", nums) // Before: [1 2 3] reassignSlice(nums) fmt.Println("After:", nums) // After: [1 2 3] }another example:
func addItem(s []int, v int) { s = append(s, v) } func main() { nums := []int{1, 2, 3} fmt.Println("Before:", nums) // Before: [1 2 3] addItem(nums, 4) fmt.Println("After:", nums) // After: [1 2 3] }
Closures
Closures are functions that capture and use variables from their surrounding scope, even after that scope ends.
Examples:
-
func sum() func(int) int { total := 0 return func(number int) int { total += number return total } } func main() { add1 := sum() add2 := sum() fmt.Println(add1(1)) // 1 fmt.Println(add1(2)) // 3 fmt.Println(add1(3)) // 6 fmt.Println(add2(1)) // 1 fmt.Println(add2(2)) // 3 fmt.Println(add2(6)) // 9 }Each call to
sum()returns a new closure with its own independent variables (total), allowingadd1andadd2to maintain separate internal state. -
func fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a } } func main() { f := fibonacci() for range 10 { fmt.Println(f()) } } // 1 // 1 // 2 // 3 // 5 // 8 // 13 // 21 // 34 // 55
The defer Keyword
The defer keyword schedules a function call or statement to execute at the end of the current function's execution.
func main() {
defer fmt.Println("done")
fmt.Println("work")
}
// work
// doneExtra:
-
deferdepends only on the surrounding function scope:deferis registered when execution reaches that line but runs when the surrounding function ends. It does not run when a block such asiforforends, even if it appears inside them.func main() { fmt.Println("start") if true { defer fmt.Println("inside if") fmt.Println("in block") } fmt.Println("end of function") } // start // in block // end of function // inside if -
Multiple defers:
Multiple deferred statements are executed in last-in-first-out (LIFO) order. The most recently deferred function runs first.
func main() { defer sayBye() fmt.Println("1") defer fmt.Println("2") fmt.Println("3") fmt.Println(myFunc()) } func myFunc() string { defer fmt.Println("4") return "5" } func sayBye() { fmt.Println("bye") } // 1 // 3 // 4 // 5 // 2 // bye