0%

Go_Learning_Fundamental_Interface

What is Interface

An Interface in go is a set of method signatures;

What’s method signature?

In my view, method signature is the abstract common represent of method; It declare method behavior and do not care abort special implement details;Here is a animal interface which include two method signatures: breathe , walk;

1
2
3
4
type animal interface {
breathe()
walk()
}

A method signature would include:

  • Name of method;
  • Number of arguments and type of each argument;
  • Number of return values and type of each return value;

How does struct implement a Interface in go?

When a type provides definition for all the methods in the interface, it is said to implement the interface; So the implement interface in go is implicit;

That is also called duck typing; In programming duck typing means we do not check type instead we check the only presence os some attributes or methods;

1
If it walks like a duck and quack like a duck then it must be duck

Declaring and implementing an interface

Like we defined before, any type implement breathe and walk method, It is said to implement a animal interfac; For example we construct a lion struct which implement animal interface;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type lion struct {
age int
}

func (l lion) breathe() {
fmt.Println("Lion breathes")
}

func (l lion) walk() {
fmt.Println("Lion walk")
}

func main() {
var a animal
a = lion{age: 10}
a.breathe()
a.walk()
}

Output

1
2
Lion breathes
Lion walk

We declare a variable of animal interface type

1
var a animal

Then we assign an instance of lion struct to it.

1
a = lion{}

Assigning an instance of lion struct to a variable of animal interface type works because lion struct implements both breathe and walk method of the animal. The type is not checked during this assignment, instead, it is enough to check that the type assigned does implement breathe and walk method. The concept is similar to duck typing, a lion can breathe and walk like an animal and hence it is an animal.

Practical use of Interface

The above example explain how to create and implement interfaces, but it didn’t really show the practical use of an interface; Instead of animal.breathe(), it would also worked if we use lion.breathe(); Now let’s look at a practical use of interface;

We will write a simple program that calculates the total expense for a company based on the individual salaries of the employees. For brevity, we have assumed that all expenses are in USD.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import (
"fmt"
)

type SalaryCalculator interface {
CalculateSalary() int
}

type Permanent struct {
empId int
basicpay int
pf int
}

type Contract struct {
empId int
basicpay int
}

//salary of permanent employee is the sum of basic pay and pf
func (p Permanent) CalculateSalary() int {
return p.basicpay + p.pf
}

//salary of contract employee is the basic pay alone
func (c Contract) CalculateSalary() int {
return c.basicpay
}

/*
total expense is calculated by iterating through the SalaryCalculator slice and summing
the salaries of the individual employees
*/
func totalExpense(s []SalaryCalculator) {
expense := 0
for _, v := range s {
expense = expense + v.CalculateSalary()
}
fmt.Printf("Total Expense Per Month $%d", expense)
}

func main() {
pemp1 := Permanent{
empId: 1,
basicpay: 5000,
pf: 20,
}
pemp2 := Permanent{
empId: 2,
basicpay: 6000,
pf: 30,
}
cemp1 := Contract{
empId: 3,
basicpay: 3000,
}
employees := []SalaryCalculator{pemp1, pemp2, cemp1}
totalExpense(employees)

}

Run in playground

Line no. 7 of the above program declares the SalaryCalculator interface with a single method CalculateSalary() int.

We have two kinds of employees in the company, Permanent and Contract defined by structs in line no. 11 and 17. The salary of permanent employees is the sum of the basicpay and pf whereas for contract employees it’s just the basic pay basicpay. This is expressed in the corresponding CalculateSalary methods in line. no 23 and 28 respectively. By declaring this method, both Permanent and Contract structs now implement the SalaryCalculator interface.

The totalExpense function declared in line no.36 expresses the beauty of interfaces. This method takes a slice of SalaryCalculator interface []SalaryCalculator as a parameter. In line no. 59 we pass a slice that contains both Permanent and Contract types to the totalExpense function. The totalExpense function calculates the expense by calling the CalculateSalary method of the corresponding type. This is done in line. no 39.

The program outputs

1
Total Expense Per Month $14050  

The biggest advantage of this is that totalExpense can be extended to any new employee type without any code changes. Let’s say the company adds a new type of employee Freelancer with a different salary structure. This Freelancer can just be passed in the slice argument to totalExpense without even a single line of code change to the totalExpense function. This method will do what it’s supposed to do as Freelancer will also implement the SalaryCalculator interface :).

Let’s modify this program and add the new Freelancer employee. Salary for the Freelancer is the product of rate per hour and total no of hours worked.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package main

import (
"fmt"
)

type SalaryCalculator interface {
CalculateSalary() int
}

type Permanent struct {
empId int
basicpay int
pf int
}

type Contract struct {
empId int
basicpay int
}

type Freelancer struct {
empId int
ratePerHour int
totalHours int
}

//salary of permanent employee is sum of basic pay and pf
func (p Permanent) CalculateSalary() int {
return p.basicpay + p.pf
}

//salary of contract employee is the basic pay alone
func (c Contract) CalculateSalary() int {
return c.basicpay
}

//salary of freelancer
func (f Freelancer) CalculateSalary() int {
return f.ratePerHour * f.totalHours
}

/*
total expense is calculated by iterating through the SalaryCalculator slice and summing
the salaries of the individual employees
*/
func totalExpense(s []SalaryCalculator) {
expense := 0
for _, v := range s {
expense = expense + v.CalculateSalary()
}
fmt.Printf("Total Expense Per Month $%d", expense)
}

func main() {
pemp1 := Permanent{
empId: 1,
basicpay: 5000,
pf: 20,
}
pemp2 := Permanent{
empId: 2,
basicpay: 6000,
pf: 30,
}
cemp1 := Contract{
empId: 3,
basicpay: 3000,
}
freelancer1 := Freelancer{
empId: 4,
ratePerHour: 70,
totalHours: 120,
}
freelancer2 := Freelancer{
empId: 5,
ratePerHour: 100,
totalHours: 100,
}
employees := []SalaryCalculator{pemp1, pemp2, cemp1, freelancer1, freelancer2}
totalExpense(employees)

}

Run in playground

We have added the Freelancer struct in line no. 22 and declared the CalculateSalary method in line no. 39. No other code change is required in the totalExpense method since Freelancer struct also implements the SalaryCalculator interface. We added a couple of Freelancer employees in the main method. This program prints,

1
Total Expense Per Month $32450  

Interface are implemented implicitly

There is no explicit declaration that a type implements an interface. In fact, in Go there doesn’t exist any “implements” keyword similar to Java. A type implements an interface if it implements all the methods of the interface.

As seen above, It is correct to define a variable of an interface type and we can assign any concrete type value to this variable if the concrete type implements all the methods of the interface.

There is no explicit declaration that says that lion struct implements the animal interface. During compilation, go notices that lion struct implements all methods of animal interface hence it is allowed. Any other type which implements all methods of the animal interface becomes of that interface type.

Let’s see a more complex example of another type implementing the animal interface.

If we define a dog struct and it implements the breathe and walk method then it will also be an animal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type lion struct {
age int
}

func (l lion) breathe() {
fmt.Println("Lion breathes")
}

func (l lion) walk() {
fmt.Println("Lion walk")
}

type dog struct {
age int
}

func (l dog) breathe() {
fmt.Println("Dog breathes")
}

func (l dog) walk() {
fmt.Println("Dog walk")
}

func main() {
var a animal

a = lion{age: 10}
a.breathe()
a.walk()

a = dog{age: 5}
a.breathe()
a.walk()
}

Output

1
2
3
4
Lion breathes
Lion walk
Dog breathes
Dog walk

Both lion and dog implement the breathe and walk method hence they are of animal type and can correctly be assigned to a variable of interface type.

The animal interface variable a was assigned a lion instance first and then the same variable was assigned a dog instance. So the type which interface variable refers to is dynamic. It dynamically holds a reference to the underlying type.

Two important points to note:

  • The interface static check is done during compile time – means that if a type doesn’t implement all the methods of an interface, then assigning the type instance to a variable of that interface type will raise an error during compile time. Eg. on deleting the walk method defined on lion struct, below error will be raised during the assignment
1
cannot use lion literal (type lion) as type animal in assignment:
  • The correct method based on the type of instance is called at run time – means that the method of either lion or dog is called depending upon whether interface variable refers to an instance of lion or dog. If it refers to an instance of lion, then lion’s method is called and if it refers to an instance of dog, then dog’s method is called. That is also proven from the output. This is a way to achieve runtime polymorphism in Go.

It is also to be noted that the methods defined by the type, should match the entire signature of methods in the interface ie., it should match

  • Name of the method

  • Number of arguments and type of each argument

  • Number of return values and type of each return value

Imagine that animal interface has another method speed which returns the int value of speed of the animal

1
2
3
4
5
type animal interface {
breathe()
walk()
speed() int
}

If the lion struct has speed method as below which does not return the int value, then the lion struct will not implement animal interface

1
func (l lion) speed()

Below compilation error will be raised on assigning the lion instance to a variable of type animal

1
2
3
4
cannot use lion literal (type lion) as type animal in assignment:
lion does not implement animal (wrong type for speed method)
have speed()
want speed() int

So in essence, method signatures are important while implementing an interface

Interface as argument to a function

A function can accept an argument of an interface type. Any type which implements that interface can be passed as that argument to that function. For example, in the below code, we have callBreathe and callWalk function which accept an argument of animal interface type. Both lion and dog instance can be passed to this function. We create an instance of both lion and dog type and pass it to the function.

It works similarly to the assignment we discussed above. During compilation no type is checked while calling the function, instead, it is enough to check that the type passed to the function does implement breathe and walk method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type lion struct {
age int
}

func (l lion) breathe() {
fmt.Println("Lion breathes")
}

func (l lion) walk() {
fmt.Println("Lion walk")
}

type dog struct {
age int
}

func (l dog) breathe() {
fmt.Println("Dog breathes")
}

func (l dog) walk() {
fmt.Println("Dog walk")
}

func main() {
l := lion{age: 10}
callBreathe(l)
callWalk(l)

d := dog{age: 5}
callBreathe(d)
callWalk(d)
}

func callBreathe(a animal) {
a.breathe()
}

func callWalk(a animal) {
a.breathe()
}

Output

1
2
3
4
Lion breathes
Lion walk
Dog breathes
Dog walk

In the above code, we have callBreathe and callWalk function which accept an argument of animal interface type. Both lion and dog instance can be passed to this function. We create an instance of both lion and dog type and pass it to the function. During compilation no type is checked while calling the function, instead, it is enough to check that the type passed to the function does implement breathe and walk method.

Non-struct Custom Type Implementing an interface

So far we have only seen examples of struct type implementing an interface. It is also perfectly ok for any non-struct custom type to implement an interface. Let’s see an example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type cat string

func (c cat) breathe() {
fmt.Println("Cat breathes")
}

func (c cat) walk() {
fmt.Println("Cat walk")
}

func main() {
var a animal

a = cat("smokey")
a.breathe()
a.walk()
}

Output

1
2
Cat breathes
Cat walk

The above program illustrates the concept that any custom type can also implement an interface. The cat is of string type and it implements the breathe and walk method hence it is correct to assign an instance of cat type to a variable of animal type.

Interface inner representation

Like any other variable, an interface variable is represented by a type and value. Interface value, in turn under the hood, consists of two tuple

  • Underlying Type
  • Underlying Value

See below diagram which illustrates what we mentioned above

image-20220123183736969

For eg in case of lion struct implementing the animal interface would be like below

image-20220123183750961

Golang provides format identifiers to print the underlying type and underlying value represented by the interface value.

  • %T can be used to print the concrete type of the interface value

  • %v can be used to print the concrete value of the interface value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type lion struct {
age int
}

func (l lion) breathe() {
fmt.Println("Lion breathes")
}

func (l lion) walk() {
fmt.Println("Lion walk")
}

func main() {
var a animal
a = lion{age: 10}
fmt.Printf("Underlying Type: %T\n", a)
fmt.Printf("Underlying Value: %v\n", a)
}

Output

1
2
Concrete Type: main.lion
Concrete Value: {10}

An interface can be embedded in other interface as well as it can be embedded in a struct. Let’s look at each one by one

Empty Interface

An interface that has zero methods is called an empty interface. It is represented as interface{}. Since the empty interface has zero methods, all types implement the empty interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
)

func describe(i interface{}) {
fmt.Printf("Type = %T, value = %v\n", i, i)
}

func main() {
s := "Hello World"
describe(s)
i := 55
describe(i)
strt := struct {
name string
}{
name: "Naveen R",
}
describe(strt)
}

Run in playground

In the program above, in line no.7, the describe(i interface{}) function takes an empty interface as an argument and hence any type can be passed.

We pass string, int and struct to the describe function in line nos. 13, 15 and 21 respectively. This program prints,

1
2
3
Type = string, value = Hello World  
Type = int, value = 55
Type = struct { name string }, value = {Naveen R}

Type assertion

Type assertion

Type assertion is used to extract the underlying value of the interface.

i.(T) is the syntax which is used to get the underlying value of interface i whose concrete type is T.

A program is worth a thousand words 😀. Let’s write one for type assertion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
)

func assert(i interface{}) {
s := i.(int) //get the underlying int value from i
fmt.Println(s)
}
func main() {
var s interface{} = 56
assert(s)
}

Run in playground

The concrete type of s in line no. 12 is int. We use the syntax i.(int) in line no. 8 to fetch the underlying int value of i. This program prints 56.

What will happen if the concrete type in the above program is not int? Well, let’s find out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
)

func assert(i interface{}) {
s := i.(int)
fmt.Println(s)
}
func main() {
var s interface{} = "Steven Paul"
assert(s)
}

Run in playground

In the program above we pass s of concrete type string to the assert function which tries to extract a int value from it. This program will panic with the message panic: interface conversion: interface {} is string, not int.

To solve the above problem, we can use the syntax

1
v, ok := i.(T)  

If the concrete type of i is T then v will have the underlying value of i and ok will be true.

If the concrete type of i is not T then ok will be false and v will have the zero value of type T and the program will not panic.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
)

func assert(i interface{}) {
v, ok := i.(int)
fmt.Println(v, ok)
}
func main() {
var s interface{} = 56
assert(s)
var i interface{} = "Steven Paul"
assert(i)
}

Run in playground

When Steven Paul is passed to the assert function, ok will be false since the concrete type of i is not int and v will have the value 0 which is the zero value of int. This program will print,

1
2
56 true  
0 false

Type switch

A type switch is used to compare the concrete type of an interface against multiple types specified in various case statements. It is similar to switch case. The only difference being the cases specify types and not values as in normal switch.

The syntax for type switch is similar to Type assertion. In the syntax i.(T) for Type assertion, the type T should be replaced by the keyword type for type switch. Let’s see how this works in the program below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
)

func findType(i interface{}) {
switch i.(type) {
case string:
fmt.Printf("I am a string and my value is %s\n", i.(string))
case int:
fmt.Printf("I am an int and my value is %d\n", i.(int))
default:
fmt.Printf("Unknown type\n")
}
}
func main() {
findType("Naveen")
findType(77)
findType(89.98)
}

Run in playground

In line no. 8 of the above program, switch i.(type) specifies a type switch. Each of the case statements compare the concrete type of i to a specific type. If any case matches, the corresponding statement is printed. This program outputs,

1
2
3
I am a string and my value is Naveen  
I am an int and my value is 77
Unknown type

89.98 in line no. 20 is of type float64 and does not match any of the cases and hence Unknown type is printed in the last line.

It is also possible to compare a type to an interface. If we have a type and if that type implements an interface, it is possible to compare this type with the interface it implements.

Let’s write a program for more clarity.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import "fmt"

type Describer interface {
Describe()
}
type Person struct {
name string
age int
}

func (p Person) Describe() {
fmt.Printf("%s is %d years old", p.name, p.age)
}

func findType(i interface{}) {
switch v := i.(type) {
case Describer:
v.Describe()
default:
fmt.Printf("unknown type\n")
}
}

func main() {
findType("Naveen")
p := Person{
name: "Naveen R",
age: 25,
}
findType(p)
}

Run in playground

In the program above, the Person struct implements the Describer interface. In the case statement in line no. 19, v is compared to the Describer interface type. p implements Describer and hence this case is satisfied and Describe() method is called.

This program prints

1
2
unknown type  
Naveen R is 25 years old

This brings us to the end of Interfaces Part I. We will continue our discussion about interfaces in Part II. Please leave your valuable feedback in the comments.

Compare with pointer receiver and value receiver

All the example interfaces we discussed in part 1 were implemented using value receivers. It is also possible to implement interfaces using pointer receivers. There is a subtlety to be noted while implementing interfaces using pointer receivers. Lets understand that using the following program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import "fmt"

type Describer interface {
Describe()
}
type Person struct {
name string
age int
}

func (p Person) Describe() { //implemented using value receiver
fmt.Printf("%s is %d years old\n", p.name, p.age)
}

type Address struct {
state string
country string
}

func (a *Address) Describe() { //implemented using pointer receiver
fmt.Printf("State %s Country %s", a.state, a.country)
}

func main() {
var d1 Describer
p1 := Person{"Sam", 25}
d1 = p1
d1.Describe()
p2 := Person{"James", 32}
d1 = &p2
d1.Describe()

var d2 Describer
a := Address{"Washington", "USA"}

/* compilation error if the following line is
uncommented
cannot use a (type Address) as type Describer
in assignment: Address does not implement
Describer (Describe method has pointer
receiver)
*/
//d2 = a

d2 = &a //This works since Describer interface
//is implemented by Address pointer in line 22
d2.Describe()

}

Run in playground

In the program above, the Person struct implements the Describer interface using value receiver in line no. 13.

As we have already learnt during our discussion about methods, methods with value receivers accept both pointer and value receivers. It is legal to call a value method on anything which is a value or whose value can be dereferenced.

p1 is a value of type Person and it is assigned to d1 in line no. 29. Person implements the Describer interface and hence line no. 30 will print Sam is 25 years old.

Similarly d1 is assigned to &p2 in line no. 32 and hence line no. 33 will print James is 32 years old. Awesome :).

The Address struct implements the Describer interface using pointer receiver in line no. 22.

If line. no 45 of the program above is uncommented, we will get the compilation error main.go:42: cannot use a (type Address) as type Describer in assignment: Address does not implement Describer (Describe method has pointer receiver). This is because, the Describer interface is implemented using a Address Pointer receiver in line 22 and we are trying to assign a which is a value type and it has not implemented the Describer interface. This will definitely surprise you since we learnt earlier that methods with pointer receivers will accept both pointer and value receivers. Then why isn’t the code in line no. 45 working.

The reason is that it is legal to call a pointer-valued method on anything that is already a pointer or whose address can be taken. The concrete value stored in an interface is not addressable and hence it is not possible for the compiler to automatically take the address of a in line no. 45 and hence this code fails.

Line no. 47 works because we are assigning the address of a &a to d2.

The rest of the program is self explanatory. This program will print,

1
2
3
Sam is 25 years old  
James is 32 years old
State Washington Country USA

Multiple Interface

A type can implement more than one interface. Lets see how this is done in the following program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
"fmt"
)

type SalaryCalculator interface {
DisplaySalary()
}

type LeaveCalculator interface {
CalculateLeavesLeft() int
}

type Employee struct {
firstName string
lastName string
basicPay int
pf int
totalLeaves int
leavesTaken int
}

func (e Employee) DisplaySalary() {
fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}

func (e Employee) CalculateLeavesLeft() int {
return e.totalLeaves - e.leavesTaken
}

func main() {
e := Employee {
firstName: "Naveen",
lastName: "Ramanathan",
basicPay: 5000,
pf: 200,
totalLeaves: 30,
leavesTaken: 5,
}
var s SalaryCalculator = e
s.DisplaySalary()
var l LeaveCalculator = e
fmt.Println("\nLeaves left =", l.CalculateLeavesLeft())
}

Run in playground

The program above has two interfaces SalaryCalculator and LeaveCalculator declared in lines 7 and 11 respectively.

The Employee struct defined in line no. 15 provides implementations for the DisplaySalary method of SalaryCalculator interface in line no. 24 and the CalculateLeavesLeft method of LeaveCalculator interface interface in line no. 28. Now Employee implements both SalaryCalculator and LeaveCalculator interfaces.

In line no. 41 we assign e to a variable of type SalaryCalculator interface and in line no. 43 we assign the same variable e to a variable of type LeaveCalculator. This is possible since e which of type Employee implements both SalaryCalculator and LeaveCalculator interfaces.

This program outputs,

1
2
Naveen Ramanathan has salary $5200  
Leaves left = 25

Embedding Interfaces

An interface can be embedded in other interface as well as it can be embedded in a struct. Let’s look at each one by one

Embedding interface in other interface

An interface can embed any number of interfaces in it as well as it can be embedded in any interface. All the methods of the embedded interface become part of the embedding interface. It is a way of creating a new interface by merging some small interfaces. Let’s understand it with an example

Assume we have an interface animal as below

1
2
3
4
type animal interface {
breathe()
walk()
}

Let’s say there is another interface named human which embeds the animal interface

1
2
3
4
type human interface {
animal
speak()
}

So if any type needs to implement the human interface, then it has to define

  • breathe() and walk() method of animal interfaces animal is embedded in human

  • speak() method of human interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type human interface {
animal
speak()
}

type employee struct {
name string
}

func (e employee) breathe() {
fmt.Println("Employee breathes")
}

func (e employee) walk() {
fmt.Println("Employee walk")
}

func (e employee) speak() {
fmt.Println("Employee speaks")
}

func main() {
var h human

h = employee{name: "John"}
h.breathe()
h.walk()
h.speak()
}

Output

1
2
3
Employee breathes
Employee walk
Employee speaks

As an other example, the ReaderWriter interface of the io package of golang (https://golang.org/pkg/io/#ReadWriter) embeds two other interfaces

1
2
3
4
type ReadWriter interface {
Reader
Writer
}

Embedding interface in a struct

An interface can be embedded in a struct as well. All the methods of the embedded interface can be called via that struct. How these methods will be called will depend upon whether the embedded interface is a named field or an unnamed/anonymous field.

  • If the embedded interface is a named field, then interface methods has to be called via the named interface name

  • If the embedded interface is unnamed/anonymous field then interface methods can be referred directly or via the interface name

Let’s see a program illustrating above points

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type dog struct {
age int
}

func (d dog) breathe() {
fmt.Println("Dog breathes")
}

func (d dog) walk() {
fmt.Println("Dog walk")
}

type pet1 struct {
a animal
name string
}

type pet2 struct {
animal
name string
}

func main() {
d := dog{age: 5}
p1 := pet1{name: "Milo", a: d}

fmt.Println(p1.name)
// p1.breathe()
// p1.walk()
p1.a.breathe()
p1.a.walk()

p2 := pet2{name: "Oscar", animal: d}
fmt.Println(p1.name)
p2.breathe()
p2.walk()
p1.a.breathe()
p1.a.walk()
}

Output

1
2
3
4
5
6
7
8
9
Milo
Dog breathes
Dod walk

Oscar
Dog breathes
Dog walk
Dog breathes
Dog walk

We declared two struct pet1 and pet2. pet1 struct has named animal interface in it

1
2
3
4
type pet1 struct {
a animal
name string
}

pet2 has unnamed/anonymous animal interface embedded

1
2
3
4
type pet2 struct {
animal
name string
}

For an instance of pet1 struct we call the breathe() and walk() method like this

1
2
p1.a.breathe()
p1.a.walk()

Directly calling these methods with raise compilation error

1
2
3
4
p1.breathe()
p1.walk()
p1.breathe undefined (type pet1 has no field or method breathe)
p1.walk undefined (type pet1 has no field or method walk)

For an instance of pet2 struct we can call the breathe() and walk() method like directly

1
2
p2.breathe()
p2.walk()

We can directly access the methods of embedded interface if the embedded interface is anonymous or unnamed.

Below is also valid and another way fo called methods of unnamed/anonymous embedded interface

1
2
p2.animal.breathe()
p2.animal.walk()

Also note that while creating the instance of either the pet1 or pet2 struct, the embedded interface i.e animal is initialised with a type implementing it i.e dog .

1
2
p1 := pet1{name: "Milo", a: d}
p2 := pet2{name: "Oscar", animal: d}

If we don’t initialise the embedded interface animal, then it will be intialised with the zero value of the interface which is nil. Calling breathe() and walk() method on such an instance of pet1 or pet2 struct will create a panic.

Access Undelying Variable of Interface

The underlying variable can be accessed in two ways

  • Type Assertion

  • Type Switch

Type assertion

Type assertion provides a way to access the underlying variable inside the interface value of the interface by asserting the correct type of underlying value. Below is the syntax for that where i is an interface.

1
val := i.({type})

The above statement is asserting that the type of underlying value in the interface is of type {type}. If this is true then the underlying value is assigned to val. If not then the above statement panics.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type lion struct {
age int
}

func (l lion) breathe() {
fmt.Println("Lion breathes")
}

func (l lion) walk() {
fmt.Println("Lion walk")
}

type dog struct {
age int
}

func (d dog) breathe() {
fmt.Println("Dog breathes")
}

func (d dog) walk() {
fmt.Println("Dog walk")
}

func main() {
var a animal

a = lion{age: 10}
print(a)

}

func print(a animal) {
l := a.(lion)
fmt.Printf("Age: %d\n", l.age)

//d := a.(dog)
//fmt.Printf("Age: %d\n", d.age)
}

Output

1
Age: 10

This is how we assert the variable a of type animal to have underlying type as lion.

1
l := a.(lion)

Below line will create a panic as underlying type is lion and not dog. Uncomment the line to check it out

1
//d := a.(dog)

Type assertion provides another way to get the underlying value and which also prevents a panic. The syntax for that is

1
val, ok := i.()

In this case type assertion returns two values, the first value is the same as discussed above, the other value is boolean indicating whether the type assertion was correct or not. This value is

  • true if the type assertion is correct meaning the type asserted is same as the underlying type

  • false if the type assertion fails.

So the second is a good way of doing type assertion since it prevents a panic. Let’s see an example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type lion struct {
age int
}

func (l lion) breathe() {
fmt.Println("Lion breathes")
}

func (l lion) walk() {
fmt.Println("Lion walk")
}

type dog struct {
age int
}

func (d dog) breathe() {
fmt.Println("Dog breathes")
}

func (d dog) walk() {
fmt.Println("Dog walk")
}

func main() {
var a animal

a = lion{age: 10}
print(a)

}

func print(a animal) {
l, ok := a.(lion)
if ok {
fmt.Println(l)
} else {
fmt.Println("a is not of type lion")
}

d, ok := a.(dog)
if ok {
fmt.Println(d)
} else {
fmt.Println("a is not of type lion")
}
}

Output:

1
2
{10}
a is not of type lion

Let’s move on to type switch now.

Type switch

Type switch enables us to do above type assertion in series. See below code example for the same

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import "fmt"

type animal interface {
breathe()
walk()
}

type lion struct {
age int
}

func (l lion) breathe() {
fmt.Println("Lion breathes")
}

func (l lion) walk() {
fmt.Println("Lion walk")
}

type dog struct {
age int
}

func (d dog) breathe() {
fmt.Println("Dog breathes")
}

func (d dog) walk() {
fmt.Println("Dog walk")
}

func main() {
var a animal

x = lion{age: 10}
print(x)

}

func print(a animal) {
switch v := a.(type) {
case lion:
fmt.Println("Type: lion")
case dog:
fmt.Println("Type: dog")
default:
fmt.Printf("Unknown Type %T", v)
}
}

Output:

1
Type: lion

In the code above, using the type switch we determine the type of value contained in interface variable x is lion or dog or some other type. It is also ok to add more different types in case statement

Zero Interface

The zero value of a interface is nil. A nil interface has both its underlying value and as well as concrete type as nil.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

type Describer interface {
Describe()
}

func main() {
var d1 Describer
if d1 == nil {
fmt.Printf("d1 is nil and has type %T value %v\n", d1, d1)
}
}

Run in playground

d1 in the above program is nil and this program will output

1
d1 is nil and has type <nil> value <nil>  

If we try to call a method on the nil interface, the program will panic since the nil interface neither has a underlying value nor a concrete type.

1
2
3
4
5
6
7
8
9
10
package main

type Describer interface {
Describe()
}

func main() {
var d1 Describer
d1.Describe()
}

Run in playground

Since d1 in the program above is nil, this program will panic with runtime error panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0xc8527]

Interface comparison

Conclusion