Functions in Go are fundamental building blocks for organizing code, performing tasks, and enabling reusability. They are defined using the func keyword and can accept parameters, return values, or both.
Defining Functions
A function declaration specifies its name, parameters, and return types. Go requires atleast one main function as the entry point.
// Function with no parameters and no return value
func sayHello() {
fmt.Println("Hello, Go!")
}
// Function with parameters and no return value
func greet(name string) {
fmt.Printf("Hello, %s!\n", name)
}
// Function with parameters and a return value
func add(x, y int) int {
return x + y
}
// Function with multiple return values
func divide(a, b int) (int, int) {
quotient := a / b
remainder := a % b
return quotient, remainder
}
func main() {
sayHello()
greet("Charlie")
sum := add(5, 3)
q, r := divide(10, 3)
fmt.Println(sum, q, r)
}
Variadic Functions
Variadic functions accept a variable number of arguments of the same type. The arguments are collected into a slice within the function. The variadic parameter must be the last one in the parameter list, and a function can have at most one such parameter.
func calculateSum(nums ...int) int {
total := 0
for _, val := range nums {
total += val
}
return total
}
func main() {
result := calculateSum(1, 2, 3, 4, 5)
fmt.Println(result) // Output: 15
}
Parameter Passing: Value vs. Reference
Parameters are passed by value by default, meaning a copy of the data is provided to the function. However, for reference types (e.g., slices, maps, channels), modifications inside the function affect the original data because they share the underlying data structure.
// Value type (array) - original unchanged
func modifyArray(arr [4]int) {
arr[0] = 100
fmt.Println("Inside function:", arr)
}
// Reference type (slice) - original modified
func modifySlice(sl []int) {
sl[0] = 100
fmt.Println("Inside function:", sl)
}
func main() {
myArray := [4]int{1, 2, 3, 4}
modifyArray(myArray)
fmt.Println("After function:", myArray) // Output: [1 2 3 4]
mySlice := []int{1, 2, 3, 4}
modifySlice(mySlice)
fmt.Println("After function:", mySlice) // Output: [100 2 3 4]
}
Variable Scope
Variables declared inside a funcsion (local variables) are only accessible within that function. Variables declared outside any function (global variables) are accessible throughout the package.
package main
import "fmt"
var globalVar = 100 // Global variable
func main() {
localVar := 50 // Local variable
if true {
innerVar := 10 // Block-scoped variable
fmt.Println(innerVar)
}
// fmt.Println(innerVar) // Error: undefined
fmt.Println(localVar)
fmt.Println(globalVar)
}
Recursive Functions
A recursive function calls itself and must have a terminating condition to avoid infinite loops.
func factorial(n int) int {
if n == 1 {
return 1
}
return n * factorial(n-1)
}
func main() {
result := factorial(5)
fmt.Println(result) // Output: 120
}
Deferred Functions
The defer statement schedules a function call to be executed after the surrounding function returns. Deferred calls are executed in LIFO order (last deferred, first executed).
func main() {
defer fmt.Println("first defer")
defer fmt.Println("second defer")
defer fmt.Println("third defer")
fmt.Println("Main body")
}
// Output:
// Main body
// third defer
// second defer
// first defer
Functions as Values
Functions are first-class types in Go. They can be assigned to variables, passed as arguments, and returned from other functions.
func add(a, b int) int {
return a + b
}
func main() {
var op func(int, int) int
op = add
result := op(3, 4)
fmt.Println(result) // Output: 7
}
Anonymous Functions
Anonymous functions have no name and can be defined inline. They are often used for closures or callbacks.
func main() {
// Assign to a variable
multiply := func(a, b int) int {
return a * b
}
fmt.Println(multiply(3, 4))
// Immediately invoked function expression (IIFE)
func(msg string) {
fmt.Println(msg)
}("Hello from anonymous!")
}
Higher-Order and Callback Functions
A higher-order function takes one or more functions as arguments or returns a function. A callback is a function passed as an argument to another function.
func operate(x, y int, callback func(int, int) int) int {
return callback(x, y)
}
func main() {
add := func(a, b int) int { return a + b }
sub := func(a, b int) int { return a - b }
resultAdd := operate(10, 5, add)
resultSub := operate(10, 5, sub)
fmt.Println(resultAdd, resultSub) // Output: 15 5
}
Closures
A closure is a function that captures variables from its outer scope. Even after the outer function finishes execution, the inner function retains access to those variables.
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
inc := counter()
fmt.Println(inc()) // 1
fmt.Println(inc()) // 2
newInc := counter()
fmt.Println(newInc()) // 1 (new counter, fresh start)
}