Introduction to Golang Methods
Go methods are basically functions with receiver arguments between the func
keyword and the name of the method. The receiver does appear in its own argument list. Go uses receivers to implement object orientation just like other languages do like python and Java where classes are used to achieve object orientation. Go does not have classes, but you can define methods on different types like struct, and non-struct types.
In Go and many other programming languages , methods are used to expose and hide properties of a type, and therefore with the help of receiver arguments , methods are used to hide and access the properties of a type.
Methods syntax
The unique thing about the above syntax is that the receiver type appears in its argument list. The receiver type declares the type the function is going to be attached to. One important thing to note is that the method and the receiver have to be in the same package. This means that Go does not allow us to declare a method and receiver whose type is defined in another package and this rule applies to built in types such as int and string.
func (receiverName receiverType) methodName (parameter List)(returnTypes){
<em> method body</em>
}
The following figure highlights the different parts involved in defining a method. It shows the quart method attached to the type gallon based receiver via the g gallon receiver parameter:
Using Method with struct type receivers
In Go, you can define a method whose receiver is a struct or a non-struct type. For most of the examples in this article, structs will be used to demonstrate methods in Go. If you haven't already, install Go runtime, and create a working directory and add main.go file in your working directory to start writing Go methods.
Example
package main
import "fmt"
type User struct {
Name string
Email string
}
func (u User) userDetails() string {
return fmt.Sprintf("User name is :%s and email is: %s", u.Name, u.Email)
}
func main() {
user1 := User{Name: "John Doe", Email: "johndoe@golinuxcloud.com"}
fmt.Println(user1.userDetails())
}
Explanation
In the preceding example, we have created a User
struct, with Name
and Email
attributes. We then define a method called userDetails()
, with the User
struct as the receiver indicated by (u User)
. In the userDetails()
method, we now have access to the receiver via the u
receiver name. We use the dot notation to access the receiver attributes and methods like so u.Name
or u.Email
. In the main function, we create an instance of the User
struct, and print the details of the user on the screen by calling the userDetails()
method on the user1
instance.
Output
$ go run main.go
User name is :John Doe and email is: johndoe@golinuxcloud.com
Using Methods with non-struct receivers
In the preceding example, we defined methods with struct types. It is possible to define methods with other types that are not struct types. One important thing to note though is that when defining a method on a type, the definition of the receiver type and of the method should be in the same package.
Example
package main
import "fmt"
type myNumber int
func (num myNumber) square() myNumber {
if num == 0 {
return 1
}
return num * num
}
func main() {
num := myNumber(25)
sq := num.square()
fmt.Printf("The square of %d is %d \n", num, sq)
}
Output
$ go run main.go
The square of 25 is 625
Using Methods with interface types
An interface in Go is a type with a defined method signature. Types with these methods defined in the interface implement the interface implicitly. With interfaces, the methods are defined both in the interface and the receiver as shown below.
Example
package main
import "fmt"
var URL string
type Protocal interface {
request(URLEndpoint string)
response() string
}
type HTTPProtocol struct {
URL string
}
func (HTTP *HTTPProtocol) request(URLEndpoint string) {
URL = fmt.Sprintf("%s/%s", HTTP.URL, URLEndpoint)
HTTP.URL = URL
}
func (http HTTPProtocol) response() string {
return URL
}
func main() {
HTTPP := HTTPProtocol{URL: "https://www.golinuxcloud.com"}
HTTPP.request("go-methods")
res := HTTPP.response()
fmt.Println(res)
fmt.Println(HTTPP)
}
Explanation
In this example, we are trying to mimic request response client server architecture. We first declare a URL
string type at the top.Then we define a Protocol
interface with two methods namely request()
and response()
. At this point, if we have a type with these methods defined, that type will implement our Protocol
interface implicitly. Then we define a HTTPProtocol
struct type with request()
and response()
methods. The request()
method has a pointer receiver, and that allows us to modify the URL
attribute of the HTTPProtocol
struct. We also assign the URL
string type variable to the new URL. In the main function, we create an instance of the HTTPProtocol
struct (HTTPP) with a URL string. We then use the request()
method to pass the URL endpoint(“go-methods”), which goes ahead to effect the changes both on the URL
variable and the URL
attribute of the HTTPProtocol
instance.
Output
$ go run main.go
https://www.golinuxcloud.com/methods
{https://www.golinuxcloud.com/methods}
Using Methods with value and pointer receivers
Go methods can accept both value and pointer receivers. In our previous example we used value receivers, in this section , we will work with both. A pointer in Go is a variable used to store the memory address of another variable, in this case we will use a pointer as a receiver. Go always passes by value , which means it creates a copy of the value in the function. If you call a method with a value receiver, and the method modifies that value, the caller will not see that modification made to the value. If you want to modify the value, the receiver has to be a pointer. In simple terms, we use pointer receivers in methods to modify the value the receiver points to and to avoid copying the value on each method call.
Learn more about pointers: Go Pointers Explained for Beginners [Practical Examples].
Example
package main
import "fmt"
type User struct {
Name string
Email string
}
func (u User) userDetails() string {
return fmt.Sprintf("User name is :%s and email is: %s", u.Name, u.Email)
}
func (u *User) changeDetails(newName, newEmail string) {
u.Name = newName
u.Email = newEmail
}
func main() {
user1 := User{Name: "John Doe", Email: "johndoe@golinuxcloud.com"}
fmt.Println(user1.userDetails())
user1.changeDetails("Mary Doe", "marydoe@golinuxcloud.com")
fmt.Println(user1.userDetails())
}
Explanation
In the above example, we add a new method called changeDetails()
with a pointer receiver to the User struct type and a newName
of type string in the parameter list. We then use the dot notation to get access to the receiver’s name and email attribute and change it to the newName
and newEmail
string respectively. In the main function, we instantiate a new user(user1) with a name and an email. We then call the changeDetails()
function on the user1
instance , which modifies the name and email to whatever name and email we pass to the changeDetails()
method. When this code is executed, we notice that the name and email of the user1
changes to a new name and email courtesy of the changeDetails()
method.
Output
$ go run main.go
User name is :John Doe and email is: johndoe@golinuxcloud.com
User name is :Mary Doe and email is: marydoe@golinuxcloud.com
Summary
In this tutorial, we have learned about what methods are and what is the difference between a method and a function. We have also dived deep into the relationship between methods, receivers and interfaces. Go methods and interfaces are important features of Go that every Go developer should have at their fingertips.
References
Golang Methods
Go methods example