Functions & Methods

Functions in Go Language

In Go, functions are segments of code designed to carry out specific tasks. They can be reused throughout the program to optimize memory usage, enhance code clarity, and save time. Functions may return a value to the caller or not, depending on their implementation.

Syntax:

func function_name(Parameter-list)(Return_type) {
    // function body...
}

Example:

package main

import "fmt"

// add() takes two integers and returns their sum
func add(x, y int) int {
    return x + y
}

func main() {
    sum := add(8, 12)
    fmt.Printf("Sum: %d", sum)
}

Output:

Sum: 20
Function Declaration

In Go, functions are defined using the func keyword, followed by the function name, a parameter list, and an optional return type.

Syntax:

func function_name(Parameter-list)(Return_type) {
    // function body...
}

Example:

func add(x, y int) int {
    return x + y
}

Output:

marks are 500 or more

Explanation:

  • func: Used to declare a function.
  • function_name: The name of the function, e.g., add.
  • Parameter-list: x, y int are the parameters with their types.
  • Return_type: int specifies the return type.
Function Calling

To execute a function, use its name followed by any required arguments in parentheses. For instance, add(8, 12) invokes the function with the arguments 8 and 12.

Example:

sum := add(8, 12)
fmt.Printf("The sum is: %d", sum)

Output:

// Go program to demonstrate
// the use of nested if statements
package main

import "fmt"

func main() {

   // Declare two variables
   var a int = 250
   var b int = 500

   // Check the first condition
   if a < 300 {

      // If condition1 is true,
      // check the nested condition
      if b < 600 {

         // Executes if both conditions are true
         fmt.Printf("a is less than 300 and b is less than 600\n")
      }
   }
}
Function Arguments

Go allows two methods for passing arguments to functions: Call by Value and Call by Reference. By default, Go employs call by value, where argument values are copied, ensuring that modifications inside the function do not affect the original variables.

1. Call by Value: In this approach, the argument values are passed as copies to the function. Any changes made to these values inside the function remain local to the function.

Example:

package main

import "fmt"

func add(x, y int) int {
    x = x + 5 // modifying x within the function
    return x + y
}

func main() {
    a := 10
    b := 20
    fmt.Printf("Before: a = %d, b = %d\n", a, b)
    total := add(a, b)
    fmt.Printf("Sum: %d\n", total)
    fmt.Printf("After: a = %d, b = %d\n", a, b)
}

Output:

Before: a = 10, b = 20
Sum: 35
After: a = 10, b = 20

2. Call by Reference: In this method, pointers are passed to the function, allowing modifications made inside the function to affect the original variables.

Example:

package main

import "fmt"

func add(x, y *int) int {
    *x = *x + 5 // modifying x via its memory address
    return *x + *y
}

func main() {
    a := 10
    b := 20
    fmt.Printf("Before: a = %d, b = %d\n", a, b)
    total := add(&a, &b)
    fmt.Printf("Sum: %d\n", total)
    fmt.Printf("After: a = %d, b = %d\n", a, b)
}

Output:

value is 150

Variadic functions in Go

Variadic functions in Go allow you to pass a flexible number of arguments to a function. This capability is particularly helpful when the exact number of arguments is unknown beforehand. A variadic function accepts multiple arguments of the same type and can handle calls with any number of arguments, including none.

Syntax:

func functionName(parameters ...Type) ReturnType {
    // Code
}

Example:

package main
import "fmt"

// Variadic function to calculate product
func product(nums ...int) int {
    result := 1
    for _, n := range nums {
        result *= n
    }
    return result
}

func main() {
    fmt.Println("Product of 2, 3, 4:", product(2, 3, 4))
    fmt.Println("Product of 6, 7:", product(6, 7))
    fmt.Println("Product with no numbers:", product())
}

Output:

Product of 2, 3, 4: 24
Product of 6, 7: 42
Product with no numbers: 1

In the syntax above:

  • parameters ...Type denotes that the function can accept a flexible number of arguments of type Type.
  • Inside the function, these arguments are accessible as a slice.
Using Variadic Functions

When defining a variadic function, include the ellipsis (...) after the parameter name, followed by the type of the arguments. These arguments are processed as a slice within the function.

Calling a Variadic Function

A variadic function can be called with any number of arguments, even none. The arguments provided are handled as a slice.

Example:

package main
import "fmt"

func product(nums ...int) int {
    result := 1
    for _, n := range nums {
        result *= n
    }
    return result
}

func main() {
    fmt.Println("Product of 1, 2, 3:", product(1, 2, 3))
    fmt.Println("Product of 5, 10:", product(5, 10))
    fmt.Println("Product with no numbers:", product())
}

Output:

Product of 1, 2, 3: 6
Product of 5, 10: 50
Product with no numbers: 1
Combining Variadic Functions with Regular Parameters

You can mix regular parameters with a variadic parameter in a single function. However, the variadic parameter must always come last in the parameter list.

Example:

package main
import "fmt"

// Function with both a regular parameter and a variadic parameter
func displayMessage(message string, numbers ...int) {
    fmt.Println(message)
    for _, n := range numbers {
        fmt.Println("Value:", n)
    }
}

func main() {
    displayMessage("Values are:", 7, 8, 9)
    displayMessage("More values:", 15, 20)
    displayMessage("No values provided:")
}

Output:

Values are:
Value: 7
Value: 8
Value: 9
More values:
Value: 15
Value: 20
No values provided:

Anonymous function in Go Language

An anonymous function is a function that lacks a name. It is particularly useful when you need to create a function inline. In Go, anonymous functions can also form closures. These are also referred to as function literals.

Syntax:

func(parameter_list)(return_type) {
    // Code block

    // Use return statement if return_type is provided.
    // If return_type is absent, do not use the return statement.
    return
}()

Example:

package main
import "fmt"

func main() {
    // Anonymous function
    func() {
        fmt.Println("Hello, World from Go!")
    }()
}

Output:

Hello, World from Go!
Assigning to a Variable

Anonymous functions can be assigned to a variable. Once assigned, the variable behaves like a regular function and can be invoked.

Syntax:

for {
    // statements...
}

Example:

// Go program demonstrating a for loop as a while loop
package main

import "fmt"

func main() {
    x := 0
    for x < 4 {
        fmt.Println("Value of x:", x)
        x++
    }
}

Output:

Value of x: 0
Value of x: 1
Value of x: 2
Value of x: 3
Passing Arguments

Anonymous functions are capable of accepting arguments.

Example:

package main
import "fmt"

func main() {
    // Passing arguments in an anonymous function
    func(greeting string) {
        fmt.Println(greeting)
    }("Hello, Go Developers!")
}

Output:

Hello, Go Developers!
Passing as Arguments

It is also possible to pass an anonymous function as an argument to another function.

Example:

package main
import "fmt"

// Passing an anonymous function as an argument
func processStrings(fn func(a, b string) string) {
    fmt.Println(fn("Hello ", "Go "))
}

func main() {
    combine := func(a, b string) string {
        return a + b + "Developers!"
    }
    processStrings(combine)
}

Output:

Hello Go Developers!
Returning Anonymous Functions

An anonymous function can also be returned from another function, allowing you to use it later.

Example:

package main
import "fmt"

// Function returning an anonymous function
func createGreeting() func(first, second string) string {
    return func(first, second string) string {
        return first + second + "is awesome!"
    }
}

func main() {
    greet := createGreeting()
    fmt.Println(greet("Go ", "Language "))
}

Output:

Go Language is awesome!

The Go language reserves two functions for special purposes: main() and init().

main() Function

In Go, the main package is a unique package used with programs that are designed to be executable. This package contains the main() function, which is a crucial function that serves as the entry point of all executable programs. The main() function neither accepts any arguments nor returns a value. It is automatically invoked by the Go runtime, so there is no need to explicitly call it. Every executable program must include exactly one main package and one main() function.

Example:

// Go program demonstrating the
// functionality of main() function

// Defining the main package
package main

// Importing necessary packages
import (
    "fmt"
    "math"
    "strings"
    "time"
)

// Main function
func main() {

    // Performing square root operation
    numbers := []float64{16, 25, 36, 49, 64}
    for _, num := range numbers {
        fmt.Printf("Square root of %.0f: %.2f\n", num, math.Sqrt(num))
    }

    // Finding the substring index
    fmt.Println("Index of substring:", strings.Index("HelloWorld", "World"))

    // Displaying the current timestamp
    fmt.Println("Current Unix time:", time.Now().Unix())
}

Output:

Square root of 16: 4.00
Square root of 25: 5.00
Square root of 36: 6.00
Square root of 49: 7.00
Square root of 64: 8.00
Index of substring: 5
Current Unix time: 1737138000
init() Function

The init() function in Go is another special function that, like main(), neither accepts arguments nor returns values. It exists in every package and is automatically invoked when the package is initialized. This function is implicitly declared and cannot be explicitly called or referenced from other parts of the program. Multiple init() functions can be defined within the same program, and they execute in the order they are declared. The execution order of init() functions across multiple files follows the lexical order of the filenames (alphabetical order). The primary purpose of the init() function is to initialize global variables or perform setup tasks that cannot be accomplished in the global scope.

Example:

// Go program demonstrating the
// behavior of init() function

// Defining the main package
package main

// Importing required package
import "fmt"

// Defining the first init() function
func init() {
    fmt.Println("First init() function executed")
}

// Defining the second init() function
func init() {
    fmt.Println("Second init() function executed")
}

// Main function
func main() {
    fmt.Println("main() function executed")
}

Output:

First init() function executed
Second init() function executed
main() function executed

Defer Keyword in Golang

In the Go programming language, defer statements postpone the execution of a function, method, or anonymous function until the surrounding function completes. In simpler terms, while the arguments of a deferred function or method call are evaluated immediately, the execution itself is deferred until the enclosing function returns. You can define a deferred function, method, or anonymous function by using the defer keyword.

Syntax:

// For a function
defer func func_name(parameter_list Type) return_type {
    // Code
}

// For a method
defer func (receiver Type) method_name(parameter_list) {
    // Code
}

// For an anonymous function
defer func(parameter_list) (return_type) {
    // Code
}()

Key Points:

  1. The Go language allows multiple defer statements in the same program, and they execute in LIFO (Last-In, First-Out) order, as illustrated in Example 2.
  2. The arguments of defer statements are evaluated immediately when the statement is encountered, but the function itself executes only when the surrounding function returns.
  3. Defer statements are commonly used for tasks like closing files, closing channels, or handling program panics gracefully.

Example:

// Go program demonstrating the concept of defer statements
package main

import "fmt"

// Function to calculate the difference
func difference(a1, a2 int) int {
    res := a1 - a2
    fmt.Println("Difference:", res)
    return 0
}

// Function to display a message
func greet() {
    fmt.Println("Welcome to Go programming!")
}

// Main function
func main() {

    // Normal call to difference() function
    difference(100, 40)

    // Deferred call to difference() function
    defer difference(200, 50)

    // Calling greet() function
    greet()
}

Output:

Difference: 60
Welcome to Go programming!
Difference: 150

Methods in Golang

Go methods are similar to functions but with a significant difference: they have a receiver argument that allows access to the receiver’s properties. The receiver can either be a struct type or a non-struct type, but both must be part of the same package. You cannot define methods for types from other packages or for built-in types like int or string, as the compiler will generate an error.

Syntax:

func (receiver_name Type) method_name(parameter_list) (return_type) {
    // Method implementation
}

Example:

package main

import "fmt"

// Defining a struct
type car struct {
    brand string
    year  int
}

// Defining a method with a struct receiver
func (c car) details() {
    fmt.Println("Brand:", c.brand)
    fmt.Println("Year:", c.year)
}

func main() {
    // Creating an instance of the struct
    vehicle := car{brand: "Toyota", year: 2022}

    // Calling the method
    vehicle.details()
}

Output:

Brand: Toyota
Year: 2022
Methods with Struct Type Receiver

When defining a method, the receiver can be a struct type. The receiver is accessible within the method. The earlier example demonstrates this with a struct type receiver.

Methods with Non-Struct Type Receiver

Go supports defining methods with non-struct type receivers, provided the type and the method definition exist in the same package. However, you cannot define methods for types from another package (e.g., int, string).

Example:

package main

import "fmt"

// Creating a custom type based on float64
type measurement float64

// Defining a method with a non-struct receiver
func (m measurement) double() measurement {
    return m * 2
}

func main() {
    value := measurement(3.5)
    result := value.double()

    fmt.Printf("Double of %.1f is %.1f\n", value, result)
}

Output:

Double of 3.5 is 7.0
Methods with Pointer Receiver

In Go, methods can also have pointer receivers, enabling modifications to the original data. This capability is unavailable with value receivers.

Syntax:

func (receiver *Type) method_name(parameters...) return_type {
    // Code to modify data
}

Example:

package main

import "fmt"

// Defining a struct
type animal struct {
    species string
}

// Method with pointer receiver to modify data
func (a *animal) rename(newSpecies string) {
    a.species = newSpecies
}

func main() {
    pet := animal{species: "Cat"}

    fmt.Println("Before:", pet.species)

    // Calling the method to rename
    pet.rename("Dog")

    fmt.Println("After:", pet.species)
}

Output:

Before: Cat
After: Dog
Methods Accepting Both Pointer and Value

In Go, methods can accept both value and pointer receivers. Depending on how the method is invoked, Go automatically handles the conversion between pointers and values.

Example:

package main

import "fmt"

type book struct {
    title string
}

// Method with pointer receiver
func (b *book) setTitle(newTitle string) {
    b.title = newTitle
}

// Method with value receiver
func (b book) displayTitle() {
    fmt.Println("Title:", b.title)
}

func main() {
    novel := book{title: "Untitled"}

    // Calling pointer receiver method with value
    novel.setTitle("1984")
    fmt.Println("After pointer method:", novel.title)

    // Calling value receiver method with pointer
    (&novel).displayTitle()
}

Output:

After pointer method: 1984
Title: 1984

Difference Between Method and Function

AspectMethodFunction
Contains a receiverYesNo
Allows same name with different typesYesNo
Usable as a first-order objectNoYes