Functions and Methods in Go

by Sabbir Ahmed


Posted on: 2 years ago


OC: Made with Vectornator
OC: Made with Vectornator

To begin with Go, we have to understand Go functions and methods. And how they differ from other languages. This article is the first of the function series.

Functions vs Methods

Single line answer would be -

  • Functions are set of instructions
  • Methods are functions that are associated with objects

Let’s understand what they mean! For example, we have user objects in JSON with the following parameters -

{
  "id": "some-user-id",
  "email": "me@sabbir.dev",
  "is_verified": false
}

Now we have to determine if the user has verified his email or not by checking the value of is_verified . We can do this both via a function or a method . let’s code…

 

/*
user object:
  {
    "id": "some-user-id",
    "email": me@sabbir.dev,
    "is_verified": false
  }
*/
package main

import "fmt"

type User struct {
        id          string
        email       string
        is_verified bool
}

func main() {
        u := User{
                id:          "some-user-id",
                email:       "me@sabbir.dev",
                is_verified: false,
        }
        functionVerified := verificationTestFunction(u)
        fmt.Println("Function Returned: ", functionVerified)

        methodVerified := u.verificationTestMethod()
        fmt.Println("Method Returned: ", methodVerified)
}

// verificationTestFunction: function example
func verificationTestFunction(u User) bool {
        return u.is_verified
}

// verificationTestMethod: method example
func (u User) verificationTestMethod() bool {
        return u.is_verified
}

At line 32 we declared a function with an explicit parameter type User and returns bool type. The function is straight forward and will work with any struct that has a parameter named is_verified of type bool .

And at line 38 we declared a method that is associated with User type. It takes no parameter as it already have the object definition. And returns bool . In Go lingo we call it value receiver method, we also have a pointer receiver method to look at —

Pointer Receiver vs Value Receiver

TL;DR: 99% of the time in production we will be using a pointer receiver

So, we have a grasp on value receiver methods, let’s assume we are tasked to write a method that will verify user email and set is_verified to true

 

/*
user object:
  {
    "id": "some-user-id",
    "email": me@sabbir.dev,
    "is_verified": false
  }
*/
package main

import "fmt"

type User struct {
        id          string
        email       string
        is_verified bool
}

func main() {
        u := User{
                id:          "some-user-id",
                email:       "me@sabbir.dev",
                is_verified: false,
        }
        u.verifyUserFromValue()
        fmt.Println("Value Receiver Method: ", u.is_verified)

        u.verifyUserFromPointer()
        fmt.Println("Pointer Receiver Method: ", u.is_verified)
}

// verifyUserFromValue: value receiver method
func (u User) verifyUserFromValue() {
        u.is_verified = true
}

// verifyUserFromPointer: pointer receiver method
func (u *User) verifyUserFromPointer() {
        u.is_verified = true
}

If we run the code the output should look like below —

Value Receiver Method:  false
Pointer Receiver Method:  true

Now, we have a value receiver function at line 33 and the output from value receiver function is not what we intended to be. Because every value receiver function copies the struct and creates a new instance of the struct for the functions use only. Any alteration of value is encapsulated within the function. So when we call u.is_verified after calling the value receiver function the value of the struct remains unchanged. Yes, we can return a value from the function and update it in the main function. But the code becomes too verbose and unmaintanable. Here comes pointer receiver methods to rescue, they take pointer of a struct and change the value globally, as it should be.

So, we should choose pointer receivers 1if we want to change the value of a struct within the method, and 2to avoid excessive memory usage by copy each time the method is called. Although for a struct small like ours, wouldn’t matter. For the sake of giving examples I have used both value receiverand pointer receiverfor the struct Userbut in real world, we should use either one of the methods, never both.

Please Note that, all struct fields, functions, and methods names are in lower case. This avoids external modules to import them, just like private functions in java, c++, c# etc.