Functions in Depth
Overview
Functions are fundamental building blocks in Go. They support multiple return values, named returns, variadic parameters, and closures.
Function Declaration
func name(params) returnType {
// body
}
func add(a, b int) int {
return a + b
}
func greet(name string) {
fmt.Println("Hello,", name)
}Multiple Return Values
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
result, err := divide(10, 2)Named Return Values
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // Naked return
}Variadic Functions
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
sum(1, 2, 3) // 6
sum([]int{1, 2, 3}...) // Spread sliceClosures
Functions can capture variables from their enclosing scope:
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
c := counter()
c() // 1
c() // 2
c() // 3Defer
Defer postpones execution until the surrounding function returns:
func readFile(name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close() // Executes when function returns
// Use file...
return nil
}Defer Order (LIFO)
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
// Output: 3, 2, 1Function Signatures
// Function type
type Operation func(int, int) int
func apply(op Operation, a, b int) int {
return op(a, b)
}
add := func(a, b int) int { return a + b }
result := apply(add, 2, 3) // 5Summary
| Feature | Syntax |
|---|---|
| Multiple returns | func f() (T1, T2) |
| Named returns | func f() (x T1, y T2) |
| Variadic | func f(args ...T) |
| Closure | func() { /* capture vars */ } |
| Defer | defer cleanup() |
More examples
Example: multiple returns and named results
Save as main.go and go run . (with go mod init example if needed).
package main
import (
"errors"
"fmt"
)
func div(a, b int) (quot int, err error) {
if b == 0 {
err = errors.New("divide by zero")
return
}
quot = a / b
return
}
func main() {
q, err := div(10, 2)
fmt.Println("10/2:", q, err)
q, err = div(10, 0)
fmt.Println("10/0:", q, err)
}Expected:
10/2: 5 <nil>
10/0: 0 divide by zero
Example: defer runs LIFO
Save as main.go and go run . (with go mod init example if needed).
package main
import "fmt"
func main() {
fmt.Println("start")
defer fmt.Println("first defer")
defer fmt.Println("second defer")
fmt.Println("end")
}Expected:
start
end
second defer
first defer
Runnable example
Save as main.go. Then:
go mod init example
go run .package main
import (
"errors"
"fmt"
)
func add(a, b int) int { return a + b }
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
fmt.Println("add:", add(2, 3))
q, err := divide(10, 2)
fmt.Println("divide ok:", q, err)
_, err = divide(1, 0)
fmt.Println("divide err:", err)
fmt.Println("sum:", sum(1, 2, 3, 4))
c := counter()
fmt.Println("counter:", c(), c(), c())
defer fmt.Println("defer: first (runs last)")
defer fmt.Println("defer: second")
fmt.Println("before return")
}Expected output:
add: 5
divide ok: 5 <nil>
divide err: division by zero
sum: 10
counter: 1 2 3
before return
defer: second
defer: first (runs last)
What to notice: Multiple returns make success and failure explicit; the closure keeps private count state; deferred calls run LIFO after the rest of main.
Try next: Add a named-return split(sum int) (x, y int) and print both values; wrap divide so callers always get a non-nil error message with the operands.