Page Content

Tutorials

Functions In Go Structure, Declaration With Code Examples

Functions In Go

As separate pieces of code that translate zero or more input parameters to zero or more output parameters, functions are essential building blocks of programs in Go. They are frequently viewed as a “black box” and are also referred to as procedures or subroutines. Because Go’s design prioritises readability, clarity, and simplicity, its features are simple to understand and maintain.

An description of Go functions, covering their different types and notions, is provided below:

Function Declaration and Basic Structure

At least one function, known as the main function, is required in every Go program since it acts as the starting point for program execution. A function’s name, a list of zero or more parameters enclosed in parenthesis, an optional return type, and a body surrounded in curly braces are all specified after the func keyword.

Example of a basic function:

package main
import "fmt" // Imports the fmt package for formatting and printing
func main() { // The special main function, execution starts here
    fmt.Println("Hello, World!") // Calls Println function from fmt package
}

Output

Hello, World!

package main: Indicates that the code is part of the main package. For executable programs, this is required.

import "fmt": Makes the fmt package accessible, enabling use of dot notation to access its functions (e.g., fmt.Println).

func main(): The primary function is defined. It returns zero values and accepts no parameters.

fmt.Println("Hello, World!"): The capitalized P in this phrase indicates that the Println function is exported from the fmt package. Its purpose is to print the string “Hello, World!” on standard output.

Parameters and Return Values

Functions can generate outputs (return values) and accept inputs (parameters/arguments).

Parameters: These are named entities that define the parameters that a function can take in a function definition. Each parameter in Go needs to have its data type specified.

Arguments: When a function is called, it receives these values as input.

Example of a function with parameters and a single return value:

func add(a int, b int) int { // Takes two int parameters, returns one int
    return a + b
}

It is possible to use a shorter syntax for parameters of the same type:

func add(a, b int) int { // Same as above, but more concise
    return a + b
}

Multiple Return Values: The ability of Go functions to return numerous different values is a powerful feature. Because functions typically return both an error and a result, this frequently makes error handling simpler.

Example of a function returning multiple values:

package main
import "fmt"
// The swap function you provided
func swap(x, y int) (int, int) {
	return y, x
}
// The required main function to run the program
func main() {
	a, b := 10, 20
	fmt.Printf("Before swap: a = %d, b = %d\n", a, b)
	a, b = swap(a, b) // Calling your swap function
	fmt.Printf("After swap: a = %d, b = %d\n", a, b)
}

Output

Before swap: a = 10, b = 20
After swap: a = 20, b = 10

Named Return Values: The function signature contains the return values’ names. The current values of the listed return variables will be automatically returned in a return statement without explicit values if return values are named.

Example with named return values:

package main
import "fmt" // Import any necessary packages
func namedMinMax(x, y int) (min, max int) {
    if x > y {
        min = y
        max = x
    } else {
        min = x
        max = y
    }
    return
}
func main() {
    // The main function is the entry point
    minVal, maxVal := namedMinMax(5, 10)
    fmt.Printf("Min: %d, Max: %d\n", minVal, maxVal)
}

Output

Min: 5, Max: 10

Discarding Values: Any undesirable return values can be thrown out using the blank identifier (_).

Variadic Functions

You can pass zero, one, or more values as a single argument to a variadic function. Variadic is indicated by the ellipsis (…) that appears before the last parameter’s type name. A slice of that type is used to represent the variadic parameter inside the function.

Example of a variadic function:

package main
import "fmt"
// This is a variadic function. It accepts a variable number of integer arguments,
// which are collected into a slice named 'numbers'.
func addAll(numbers ...int) int {
	total := 0
	for _, num := range numbers {
		total += num
	}
	return total
}
func main() {
	// --- Usage Examples ---
	// 1. Pass multiple integers directly to the function
	fmt.Println("Sum of 1, 2, 3:", addAll(1, 2, 3))
	// 2. Pass zero integers
	fmt.Println("Sum of zero integers:", addAll())
	// 3. Pass a slice by "exploding" it with '...'
	nums := []int{10, 20, 30}
	fmt.Println("Sum of slice", nums, "is:", addAll(nums...))
}

Output

Sum of 1, 2, 3: 6
Sum of zero integers: 0
Sum of slice [10 20 30] is: 60

They are **copied. This indicates that changes made to parameters inside the function have no effect on the variables that were initially in the calling function.

Pointers (Pass by Reference): You give a pointer to a function (pass by reference) so that it can change the original value.

Example illustrating pass by value vs. pointers:

package main
import "fmt"
func zeroByValue(x int) {
	// This modifies a copy of the argument.
	x = 0
}
func zeroByPointer(xPtr *int) {
	// This dereferences the pointer to modify the original value.
	*xPtr = 0
}
func main() {
	// --- Pass by Value ---
	x := 5
	fmt.Printf("Before zeroByValue: x = %d\n", x)
	zeroByValue(x)
	fmt.Printf("After zeroByValue: x = %d (original value is unchanged)\n\n", x)
	// --- Pass by Pointer ---
	y := 5
	fmt.Printf("Before zeroByPointer: y = %d\n", y)
	zeroByPointer(&y) // The '&' operator gets the memory address of y
	fmt.Printf("After zeroByPointer: y = %d (original value is modified)\n", y)
}

Output

Before zeroByValue: x = 5
After zeroByValue: x = 5 (original value is unchanged)
Before zeroByPointer: y = 5
After zeroByPointer: y = 0 (original value is modified)

Call Stack

The call stack is where Go functions work. A function is popped off the call stack after it returns, and it is put onto the call stack when it is invoked.

Methods

A unique kind of function in Go that belongs to a certain type is called a method. Similar to a parameter, its “receiver” appears in the declaration of the method before its name. When calling methods on an instance of the receiver type, dot notation is used (e.g., myCircle.area()).

Example of a method declaration:

package main // This is the required package declaration
import "fmt"
type Circle struct {
    radius float64
}
func (c Circle) area() float64 { // 'c Circle' is the receiver
    return 3.14159 * c.radius * c.radius
}
func main() {
    myCircle := Circle{radius: 10}
    fmt.Println(myCircle.area())
}

Output

314.159

A method has a receiver, whereas a function does not. This is the main distinction. Go’s object-oriented design methodology relies heavily on methods, which give types behaviour in place of conventional classes and inheritance.

First-Class Functions, Anonymous Functions, and Closures

As “first-class” types in Go, functions can be supplied as arguments to other functions, allocated to variables, and returned as the outcomes of other functions.

Anonymous Functions (Function Literals): There is no name for these functions. They are frequently employed for minor, regional jobs.

Example of an anonymous function:

package main // This is the required package declaration
import "fmt"
func main() {
    // The rest of your code goes here
    add := func(x, y int) int {
        return x + y
    }
    fmt.Println(add(1, 1))
}

Output

2

Closures: If an anonymous function refers to (and has the ability to change) non-local variables from its immediate scope, it is a closure. These recorded variables are retained in between closure calls.

Example of a closure (makeEvenGenerator):

package main
import (
	"fmt"
)
// --- Closures ---
// A function that returns another function. The inner function "closes over" the variable 'i'.
func makeEvenGenerator() func() uint {
	i := uint(0)
	return func() uint {
		ret := i
		i += 2
		return ret
	}
}
// --- Recursion ---
// A function that calls itself.
func factorial(x uint) uint {
	// Base Case: stops the recursion
	if x == 0 {
		return 1
	}
	// Recursive Step: the function calls itself with a modified argument
	return x * factorial(x-1)
}
func main() {
	fmt.Println("--- Demonstrating Closures ---")
	// The variable 'nextEven' now holds the returned anonymous function.
	nextEven := makeEvenGenerator()
	fmt.Println(nextEven()) 
	fmt.Println(nextEven()) 
	fmt.Println(nextEven()) 
	fmt.Println("\n--- Demonstrating Recursion ---")
	// The factorial function calls itself to calculate the result.
	fmt.Println("Factorial of 5 is:", factorial(5)) 
	fmt.Println("\n--- Demonstrating Defer ---")
	// Deferred functions are pushed onto a stack and executed when the surrounding
	// function (in this case, main) returns. They execute in LIFO (Last-In, First-Out) order.
	defer fmt.Println("1st deferred statement")
	defer fmt.Println("2nd deferred statement")
	fmt.Println("Main function is executing statements...")
	
}

Output

--- Demonstrating Closures ---
0
2
4
--- Demonstrating Recursion ---
Factorial of 5 is: 120
--- Demonstrating Defer ---
Main function is executing statements...
2nd deferred statement
1st deferred statement

panic: A runtime error is raised by this built-in function, which instantly ends the function and any calling functions, causing the application to crash. Usually, it signals a programming problem or an extraordinary circumstance that cannot be fixed.

recover: This built-in feature prevents a panic from spreading up the call stack. The recover function is typically invoked inside a defer function.

Example of panic and recover:

package main
import "fmt"
func examplePanic() {
	defer func() {
		// recover() catches the panic and returns the panic value.
		// If no panic occurred, it returns nil.
		if r := recover(); r != nil {
			fmt.Println("Recovered in examplePanic:", r)
		}
	}()
	fmt.Println("About to panic!")
	panic("Something went wrong!") // Initiates a panic
	fmt.Println("This line will not be executed.")
}
func main() {
	examplePanic()
	fmt.Println("Program continues after panic was recovered.")
}

Output

About to panic!
Recovered in examplePanic: Something went wrong!
Program continues after panic was recovered.

Go’s fundamental idea of functions makes it possible to write modular, reusable, and maintainable code. It is essential to comprehend these elements in order to write efficient Go programs.

You can also read Understanding Arrays In Golang, It’s Types And Declaration

Agarapu Geetha
Agarapu Geetha
My name is Agarapu Geetha, a B.Com graduate with a strong passion for technology and innovation. I work as a content writer at Govindhtech, where I dedicate myself to exploring and publishing the latest updates in the world of tech.
Index