The Math and Code Behind…
Facial alignment is a prereq…
3 years, 5 months ago
by Sabbir Ahmed
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.
Single line answer would be -
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 —
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 —
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 1
if we want to change the value of a struct within the method, and 2
to 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 receiver
and pointer receiver
for the struct User
but 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.