Getting started with golang interfaces
In Go , interfaces are custom types that define a set of function signatures. By design , interfaces are considered to be tools that make code clean, short and provide a good API between packages, servers and clients. Go interfaces enable developers to treat other types as one type temporarily as long as these types have the functions defined in the interface. When a type has the functions defined in an interface, we say that that type implements that interface.
When using interfaces , you are not allowed to create an instance of an interface, but rather, you can create a variable of an interface type and this variable can be assigned to a value type that implements the interface.
Therefore Go interfaces are super important for Go developers to have in their tool belt. In this article we will learn about Go interfaces and therefore it’s a prerequisite that you have Go runtime installed in your computer to be able to proceed without any problems.
Golang interface syntax
It’s important to remember that Go interfaces are simply a collection of
named function signatures wrapped inside a custom type(interface).To
define an interface use the keywordtype followed by the name of the
interface and lastly the keywordinterfaceand curly braces. Using
your preferred code editor, navigate to your working directory and
create a folder called interfaces and add a main.go file in your
interface directory and add the below code.
package main
type salaryCalculator interface{}
When naming your interface, the name should describe what the interface is doing. It has become a popular thing in the Go community to use the “er” suffix to the interface name where possible.
Empty interface
An empty interface is an interface that does not specify methods in it.
This means that an empty interface may hold values of any type.
Therefore it’s worth noting that in Go , every type implements at least
zero methods. Empty interfaces are useful with code that handles values
of unknown type. A good example is from thefmt package,
wherefmt.Print()function takes any number of arguments of
typeinterface{}.The zero value of an empty interface in Go
isnilas shown below.
Example
package main
import "fmt"
type salaryCalculator interface{}
func main() {
var s salaryCalculator
fmt.Println(s)
}
Output
$ go run main.go
<nil>
Interface with methods set
To add a named function signature, add the name of the function followed by round parenthesis and a return value if the function has one. Our interface has no function signature in it. To add a named method signature, add the name of the function followed by round parenthesis and a return value if the function has one.
Example
package main
type salaryCalculator interface {
calculateSalary() float64
report()
}
Explanation
In the above example, we have defined asalaryCalculatorinterface that
has two function signatures calledcalculateSalary()andreport().
To use (implement) the interface, we need to define a type that has the
two functions. It’s common to define type
structs in Go to
implement interfaces, but other types also like string can also be used
with interfaces.
Implementing an interface in golang
Example-1
In the next example, we have defined two structs
namelyPermanentEmployeeandContractEmployee. These two struct
types define receiver functions with the same names (calculateSalary &
report ) and return type as the once in
thesalaryCalculatorinterface. By defining receiver functions with
the same name and return types as the once in the interface, will
implements the interface implicitly. This is a unique way in Go that
allows developers to implement interfaces without using keywords like
“implement” . The receiver functions
forPermanentEmployeeandContractEmployeehave the same
signatures but different implementations. Therefore the salary
calculation forPermanentEmployeeis different from
theContractEmployeebut they all perform the same task.
package main
import "fmt"
type salaryCalculator interface {
calculateSalary() float64
report()
}
type PermanentEmployee struct {
id int
basicSalary float64
commission float64
}
type ContractEmployee struct {
id int
basicSalary float64
}
func (p PermanentEmployee) calculateSalary() float64 {
return p.basicSalary + (p.commission/100)*p.basicSalary
}
func (c ContractEmployee) calculateSalary() float64 {
return c.basicSalary
}
func (p PermanentEmployee) report() {
fmt.Printf("Employee ID %d earns USD %f per month \n", p.id, p.calculateSalary())
}
func (c ContractEmployee) report() {
fmt.Printf("Employee ID %d earns USD %f per month \n", c.id, c.calculateSalary())
}
func main() {
var calculator salaryCalculator
calculator = PermanentEmployee{id: 1, basicSalary: 10000, commission: 20}
calculator.report()
calculator = ContractEmployee{id: 2, basicSalary: 5000}
calculator.report()
}
Output:
$ go run main.go
Employee ID 1 earns USD 12000.000000 per month
Employee ID 2 earns USD 5000.000000 per month
Explanation:
In our main function, we define a calculator variable of
typesalaryCalculator. In the next line, we assign the calculator
variable toPermanentEmployee struct. This is made possible due to the
fact thatPermanentEmployeeimplements
thesalaryCalculatorinterface.The same applies for
theContractEmployeestruct.When working with
thesalaryCalculatorin our example, we see
thatPermanentEmployeeandContractEmployeeare one type because
they share same behavior and thesalaryCalculatorinterface links
them up. The output on the terminal prints a message for each employee.
But how is this useful? Imagine you have a slice of employees , both
permanent and contract , and the company they work for wants to
calculate the total salary that will be paid at the end of the month.
It’s true that it’s hectic to loop through all employees and do the math
on the fly while in the loop.
Example-2
To solve this , we can create a slice of typesalaryCalculatorand
add employees in the slice , loop through them and calculate the total
salary.
Example
package main
import "fmt"
type salaryCalculator interface {
calculateSalary() float64
report()
}
type PermanentEmployee struct {
id int
basicSalary float64
commission float64
}
type ContractEmployee struct {
id int
basicSalary float64
}
func (p PermanentEmployee) calculateSalary() float64 {
return p.basicSalary + (p.commission/100)*p.basicSalary
}
func (c ContractEmployee) calculateSalary() float64 {
return c.basicSalary
}
func (p PermanentEmployee) report() {
fmt.Printf("Employee ID %d earns USD %f per month \n", p.id, p.calculateSalary())
}
func (c ContractEmployee) report() {
fmt.Printf("Employee ID %d earns USD %f per month \n", c.id, c.calculateSalary())
}
func main() {
p1 := PermanentEmployee{id: 1, basicSalary: 2300, commission: 13}
p2 := PermanentEmployee{id: 2, basicSalary: 1500, commission: 18}
p3 := PermanentEmployee{id: 3, basicSalary: 2300, commission: 10}
c1 := ContractEmployee{id: 4, basicSalary: 500}
c2 := ContractEmployee{id: 5, basicSalary: 1100}
c3 := ContractEmployee{id: 6, basicSalary: 700}
employees := []salaryCalculator{p1, p2, p3, c1, c2, c3}
var totalSalary float64
for _, employee := range employees {
totalSalary += employee.calculateSalary()
}
fmt.Printf("Company total salary is : USD %f", totalSalary)
}
Output
$ go run main.go
Company total salary is USD 9199.000000
Example-3
To make our code shorter and cleaner, we can define asalaryExpense()
function that takes a slice of salaryCalculator interface as an
argument, and return the total salary. Add the below code to the
existing code.
Example
func salaryExpense(s []salaryCalculator) float64 {
var totalSalary float64
for _, employee := range s {
totalSalary += employee.calculateSalary()
}
return totalSalary
}
func main() {
p1 := PermanentEmployee{id: 1, basicSalary: 2300, commission: 13}
p2 := PermanentEmployee{id: 2, basicSalary: 1500, commission: 18}
p3 := PermanentEmployee{id: 3, basicSalary: 2300, commission: 10}
c1 := ContractEmployee{id: 4, basicSalary: 500}
c2 := ContractEmployee{id: 5, basicSalary: 1100}
c3 := ContractEmployee{id: 6, basicSalary: 700}
employees := []salaryCalculator{p1, p2, p3, c1, c2, c3}
totalSalary := salaryExpense(employees)
fmt.Printf("Company total salary is USD %f \n", totalSalary)
}
Output
$ go run main.go
Company total salary is USD 9199.000000
Example-4: Create built-in error interface
erroris a built-in interface type in Go. A variable from the error
interface represents any value that describes itself as a string.The
error type has a single method called Error(). In Go , the error
interface is declared as below.
type error interface {
Error() string
}
Go does not have the try catch error handling mechanism, and the way around that is to handle errors as they appear. For example, when working with files in Go, it is good to check if the file can be read from. In this scenario, we will use the built-in error interface to handle the error. In the next example, we will create our custom type that implements the error built-in type.
Example
package main
import (
"fmt"
"os"
)
type ReadFileError struct {
message string
}
func (rfe ReadFileError) Error() string {
return fmt.Sprintf("Custom Read File Error! Error Message : %s", rfe.message)
}
func errorHandler(err error) {
fmt.Println(err.Error())
}
func main() {
_, err := os.ReadFile("logs.txt")
if err != nil {
e := ReadFileError{message: "Error reading file"}
errorHandler(e)
return
}
fmt.Println("Reading data from file")
}
Explanation
In our example, we have a created custom struct
typeReadFileError.ReadFileError has a receiver function
calledError()and it returns a string, which
makesReadFileErrorstruct to implement the built-in error interface
implicitly. We then define theerrorHandler function that takes the
built-in error type as its argument. This function only prints messages
on the screen by callingerr.Error()function.In the main function,
we try to
read a
file called“logs.txt” that does not exist in our root folder.
This will automatically return an error. We handle the error by creating
our custom messagee := ReadFileError{message: "Error reading file"}
then useerrorHandler(e)To print the error in the terminal.
Output
$ go run main.go
Custom Read File Error! Error Message : Error reading file
Summary
Go interface is an important tool to use as Go developer. Interfaces in Go enables developers to group together types as long as these types have the same methods defined in an interface that they are implementing. Many times Go interfaces are used to force encapsulation and allow for a versatile, clean and short code base.
References
https://go.dev/tour/methods/9
https://gobyexample.com/interfaces
Related Keywords: golang interface, go interface, interface golang, interfaces in golang, interface in go, golang interface type, golang interfaces

![Golang interface Tutorial [Practical Examples]](/golang-interface/golang_interface.jpg)
