Methods in Golang
In Golang methods act on types. They are very similar to regular functions except that they take a receiver as a parameter. The receiver is the type which the method will be attached to. This will allow us to do something like myTypevar.doSomething().
For those coming from a language with classes and objects methods in Golang are similar to class member methods. As with regular functions you can pass the receiver by value or by reference. By convention a type’s methods should all be either value or reference parameters. We usually don’t have some methods with value and some with reference parameters.
Example 1: value receiver
https://go.dev/play/p/2urDh8IkY3c
Here’s our first super simple example using a value parameter:
// simple program to demonstrate methods on types
package main
import "fmt"
type myInt int
func (m myInt) addTwo() myInt {
return m + 2
}
func main() {
t := myInt(2)
fmt.Printf("t is %d\r\n", t)
// call the addTwo method
t = t.addTwo()
fmt.Printf("now t is %d\r\n", t)
}
In the above example we create a new type called myInt based on an integer. Note that it starts with a lowercase letter so this is an unexported type, meaning it can only be using in this package. Also, the method, addTwo, is also unexported.
Because our method uses a value receiver we have to return a value and in our main body code we have to reassign the value of t.
Example 2: reference receiver
https://go.dev/play/p/H6Z-P1I5w-8
It would be handy if we didn’t have to reassign the variable (t = t.addTwo()). We can achieve this by using a reference as the receiver. As with regular functions using reference parameters we achieve this using the * operator.
In this example our receiver is a reference type so we can simply call the method without a reassignment.
// simple program to use a reference receiver on the method
package main
import "fmt"
type myType int
func (m *myType) double() {
*m *= 2
}
func main() {
t := myType(5)
fmt.Printf("t is %d\r\n", t)
t.double()
fmt.Printf("Now t is %d\r\n", t)
}
Example 3: Validate host name / ip address
https://go.dev/play/p/OI-aeCrQMCd
In this example which is a more realistic example we’ll have a Host type and we’ll add some methods to validate that the host is valid:
// Validate host
package main
import (
"fmt"
"net"
"net/url"
"regexp"
)
type myHost string
func (h myHost) isValidHost() bool {
if h.isValidIPv4() {
return true
} else if h.isValidIPv6() {
return true
} else if h.isValidURL() {
return true
}
return false
}
func (h myHost) isValidIPv4() bool {
parsedIP := net.ParseIP(string(h))
return parsedIP != nil && parsedIP.To4() != nil
}
func (h myHost) isValidIPv6() bool {
re := regexp.MustCompile(`^(([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)|(([0-9a-fA-F]{1,4}:){1,7}:)|(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})|(([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2})|(([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3})|(([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4})|(([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5})|([0-9a-fA-F]{1,4}:)((:[0-9a-fA-F]{1,4}){1,6})|(:((:[0-9a-fA-F]{1,4}){1,7}|:))|(::)|(([0-9a-fA-F]{1,4}:){1,4}:[0-9a-fA-F]{1,4})|(([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,6}))$`)
return re.MatchString(string(h))
}
func (h myHost) isValidURL() bool {
parsedURL, err := url.Parse(string(h))
if err != nil {
return false
}
// Check that the scheme is either http or https
if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
return false
}
// Check that the host is not empty
if parsedURL.Host == "" {
return false
}
return true
}
func main() {
// Specific check for IPv4
host := myHost("192.168.0.1")
fmt.Printf("host %s is a valid IPv4? %t\r\n", host, host.isValidIPv4())
// Specific check for IPv6
host = myHost("2a01:235:133::1")
fmt.Printf("host %s is a valid IPv6? %t\r\n", host, host.isValidIPv6())
// Specific check for url
host = myHost("http://example.com")
fmt.Printf("host %s is a valid URL? %t\r\n", host, host.isValidURL())
// Specific check for url
host = myHost("https://www.example.com")
fmt.Printf("host %s is a valid URL? %t\r\n", host, host.isValidURL())
// Generic host check
host = myHost("somethingelse")
fmt.Printf("host %s is a valid host? %t\r\n", host, host.isValidHost())
// Generic host check
host = myHost("345.34.33.33")
fmt.Printf("host %s is a valid host? %t\r\n", host, host.isValidHost())
// Generic host check
host = myHost("34.34.33.33")
fmt.Printf("host %s is a valid host? %t\r\n", host, host.isValidHost())
// Generic host check
host = myHost("https://www.example.com")
fmt.Printf("host %s is a valid host? %t\r\n", host, host.isValidHost())
// Generic host check
host = myHost("https://www.example.com/mypage")
fmt.Printf("host %s is a valid host? %t\r\n", host, host.isValidHost())
}
In this example we have a myHost custom type based on a string. It has several methods defined:
- isValidIPv4
- isValidIPv6
- isValidURL
- isValidHost
The first three methods are specific checks for IPv4/6 and a url name like http://example.com. The final check checks for either a url or an IPv4/6.
Conclusion
The example demonstrates how to have multiple methods on your golang custom types and also how to call methods from within a method.
Conclusion
Golang makes it easy to add methods onto your custom types. These can use value or reference receivers.
John, a seasoned Freelance Full Stack Developer based in South Africa, specialises in delivering bespoke solutions tailored to your needs. With expertise in back end languages and frameworks, PHP, Laravel and Golang and Front end frame words Vue3, Nuxt3 as well as Angular, I am equipped to tackle any project, ensuring robust, scalable, and cutting-edge outcomes.
My comprehensive skill set enables me to provide exceptional freelance services both remotely and in person. Whether you’re seeking to develop an innovative application or require meticulous refinement of existing systems, I am dedicated to elevating your digital presence through unparalleled technical prowess and strategic development methodologies. Let’s connect to transform your vision into reality.