Saturday, February 7, 2026

Functions

    Profile Pic of Akash AmanAkash Aman

    Updated: February 2026

    Functions

    The Basics

    Concept: Functions are the building blocks of Go logic. In Go, functions are first-class citizens, meaning they can be assigned to variables, passed as arguments, and returned from other functions.

    • Why: Go favors composition and simplicity. By treating functions as values, Go allows for powerful patterns like middleware and decorators without needing complex Object-Oriented hierarchies.
    go
    // Standard Function
    func add(a int, b int) int {
        return a + b
    }
    
    // Type Grouping (Cleaner syntax for same-type parameters)
    func multiply(a, b int) int {
        return a * b
    }
    
    func main() {
        result := add(5, 10)
        fmt.Println(result)
    }
    

    Multiple Return Values

    Concept: A function can return any number of values.

    • Why: This is Go’s idiomatic way of handling errors. Instead of "throwing exceptions" which can be hidden, Go forces you to receive an error as a value and deal with it immediately.

    • Behavior: By returning (value, error), Go makes the "happy path" and the "error path" equally visible. You cannot accidentally ignore a return value without using the blank identifier _.

    go
    func divide(a, b float64) (float64, error) {
        if b == 0 {
            return 0, errors.New("cannot divide by zero")
        }
        return a / b, nil
    }
    
    // Usage
    val, err := divide(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    

    Named & Naked Returns

    Concept: You can name the variables in the function signature's return block.

    • Why: Named returns document the meaning of the values (e.g., (rect int, err error) is clearer than (int, error)).

    • Behavior: If you name the returns, a simple return keyword (a "naked" return) will automatically return the current values of those variables.

    go
    func split(sum int) (x, y int) {
        x = sum * 4 / 9
        y = sum - x
        return // Returns x and y automatically
    }
    

    Variadic Functions

    Concept: A function that accepts any number of trailing arguments of a specific type using the ... operator.

    • Behavior: Inside the function, the variadic parameter is treated as a Slice.
    go
    func sumAll(nums ...int) int {
        total := 0
        for _, n := range nums {
            total += n
        }
        return total
    }
    
    func main() {
        fmt.Println(sumAll(1, 2, 3, 4)) // 10
        
        // Unpacking a slice into a variadic function
        mySlice := []int{10, 20}
        fmt.Println(sumAll(mySlice...)) 
    }
    

    Anonymous Functions & Closures

    Concept: You can define a function without a name (Anonymous) to execute it immediately or assign it to a variable.

    • Behavior: A Closure is an anonymous function that "closes over" variables in its scope, remembering them even after the outer function finishes.

    • Memory Note: When a closure references an outer variable, Go moves that variable from the Stack to the Heap (Escape Analysis) so it survives as long as the closure needs it.

    go
    func sequence() func() int {
        i := 0
        return func() int {
            i++ // Accesses and increments 'i' from the outer scope
            return i
        }
    }
    

    Behavioral Reason: Decoupling Logic

    Concept: Using functions as arguments allows for Inversion of Control. Instead of a function deciding how to process data, it asks the caller to provide the logic.

    • Why: This is the foundation of Go's http.HandlerFunc and middleware patterns. It allows you to write generic code that doesn't care about the specific logic inside.
    go
    // filter is generic; it only cares that 'logic' takes an int and returns a bool
    func filter(numbers []int, logic func(int) bool) []int {
        var result []int
        for _, n := range numbers {
            if logic(n) {
                result = append(result, n)
            }
        }
        return result
    }
    

    The Crux: Memory & Type

    • Function Signature as Type: The parameters and returns define the type. func(int) int is a different type than func(string) bool.
    • Memory Efficiency: When passing functions, you aren't copying code. You are passing a Pointer (8 bytes) to the function's entry point.
    • Flexibility: This system allows you to swap logic at runtime and keeps your codebase modular.

    Higher-Order Functions & Middleware

    Concept: A Higher-Order Function is a function that either takes a function as an argument, returns a function, or both.

    • Why: In web development, we use this to create Middleware. Instead of writing "logging" or "authentication" logic inside every single function, you "wrap" your main logic with a middleware function.

    • Behavior: The outer function (the wrapper) executes some code, then calls the inner function, and then can execute more code. It’s like an onion with layers of logic.

    go
    // 1. The core logic function
    func processData(taskID int) int {
        fmt.Printf("Processing task %d...\n", taskID)
        return taskID * 2
    }
    
    // 2. The Middleware (Higher-Order Function)
    // It takes a function and returns a "wrapped" version of it
    func loggerMiddleware(next func(int) int) func(int) int {
        return func(n int) int {
            fmt.Println("[Log] Start operation")
            result := next(n) // Call the original function
            fmt.Println("[Log] End operation")
            return result
        }
    }
    
    func main() {
        // Wrap the original function
        wrappedProcess := loggerMiddleware(processData)
    
        // Run the wrapped version
        wrappedProcess(42)
    }
    

    The "Wrapping" Execution Flow

    When you call a wrapped function, the execution follows a specific path through the memory stack. This is why it is so powerful for things like timing how long a function takes to run.

    StepActionResponsibility
    1EntryMiddleware starts (e.g., starts a timer or logs "User connected").
    2Hand-offMiddleware calls the next() function (the core logic).
    3ExecutionCore function does the actual work (e.g., saving to a database).
    4ReturnControl returns to the Middleware to finish (e.g., logs "Success").

    Real-World Use: The HTTP Handler

    In Go’s standard library, this is exactly how web servers work. The http.HandlerFunc is just a function type, allowing you to chain logic together easily.

    go
    func authMiddleware(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            if r.Header.Get("Token") == "" {
                http.Error(w, "Unauthorized", 401)
                return
            }
            next.ServeHTTP(w, r)
        })
    }
    

    Β© 2026 Akash Aman | All rights reserved