In the past few months, I have been conducting a 
study asking people that it’s hard for them to understand in Go.  And I noticed that the answers regularly mentioned the concept of interfaces.  Go was the first interface language I used, and I remember that at that time this concept seemed very confusing.  And in this guide, I want to do this: 
      
        
        
        
      
    
      
        
        
        
      
    -   To explain in human language what interfaces are. 
 -   Explain how they are useful and how you can use them in your code. 
 -  Talk about what 
interface{}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     (an empty interface).  -   And walk through several useful interface types that you can find in the standard library. 
 
      
        
        
        
      
      So what is an interface? 
      
        
        
        
      
      The interface type in Go is a kind of 
definition .  It defines and describes the specific methods that 
some other type should have . 
      
        
        
        
      
    
      
        
        
        
      
      One of the interface types from the standard library is the 
fmt.Stringer interface: 
      
        
        
        
      
    
      
        
        
        
      
     type Stringer interface { String() string }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      We say that something 
satisfies this interface (or 
implements this interface ) if this “something” has a method with a specific signature string value 
String()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     . 
      
        
        
        
      
    
      
        
        
        
      
      For example, the 
Book
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     type satisfies the interface because it has the 
String()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     string method: 
      
        
        
        
      
    
      
        
        
        
      
     type Book struct { Title string Author string } func (b Book) String() string { return fmt.Sprintf("Book: %s - %s", b.Title, b.Author) }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      It doesn't matter what type the 
Book
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     or what it does.  All that matters is that it has a method called 
String()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     that returns a string value. 
      
        
        
        
      
    
      
        
        
        
      
      Here is another example.  The 
Count
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     type also 
satisfies the fmt.Stringer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     interface because it has a method with the same signature string value 
String()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     . 
      
        
        
        
      
    
      
        
        
        
      
     type Count int func (c Count) String() string { return strconv.Itoa(int(c)) }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      It is important to understand here that we have two different types of 
Book
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     and 
Count
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     , which act differently.  But they are united by the fact that they both satisfy the 
fmt.Stringer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     interface. 
      
        
        
        
      
    
      
        
        
        
      
      You can look at it from the other side.  If you know that the object satisfies the 
fmt.Stringer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     interface, then you can assume that it has a method with the signature string value 
String()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     that you can call. 
      
        
        
        
      
    
      
        
        
        
      
      And now the most important thing. 
      
        
        
        
      
    
      
        
        
        
      
      When you see a declaration in Go (of a variable, function parameter, or structure field) that has an interface type, you can use an object of any type as long as it satisfies the interface. 
      
        
        
        
      
    
      
        
        
        
      
      Let's say we have a function: 
      
        
        
        
      
    
      
        
        
        
      
     func WriteLog(s fmt.Stringer) { log.Println(s.String()) }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Since 
WriteLog()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     uses the interface type 
fmt.Stringer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     in the parameter 
fmt.Stringer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     , we can pass any object that satisfies the 
fmt.Stringer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     interface.  For example, we can pass the 
Book
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     and 
Count
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     types that we created earlier in the 
WriteLog()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     method, and the code will work fine. 
      
        
        
        
      
    
      
        
        
        
      
      In addition, since the passed object satisfies the 
fmt.Stringer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     interface, we 
know that it has a 
String()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     method, which can be safely called by the 
WriteLog()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     function. 
      
        
        
        
      
    
      
        
        
        
      
      Let's put it all together in one example, demonstrating the power of interfaces. 
      
        
        
        
      
    
      
        
        
        
      
     package main import ( "fmt" "strconv" "log" ) 
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      That's cool.  In the main function, we created different types of 
Book
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     and 
Count
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     , but passed them to the 
same WriteLog()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     function.  And she called the appropriate 
String()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     functions and wrote the results to the log. 
      
        
        
        
      
    
      
        
        
        
      
      If you 
execute the code , you will get a similar result: 
      
        
        
        
      
    
      
        
        
        
      
     2009/11/10 23:00:00 Book: Alice in Wonderland - Lewis Carrol 2009/11/10 23:00:00 3
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      We will not dwell on this in detail.  The main thing to remember: using the interface type in the declaration of the 
WriteLog()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     function, we made the function indifferent (or flexible) to the 
type of the received object.  What matters is 
what methods he has . 
      
        
        
        
      
    
      
        
        
        
      
      What are useful interfaces? 
      
        
        
        
      
      There are a number of reasons why you can start using interfaces in Go.  And in my experience, the most important ones are: 
      
        
        
        
      
    
      
        
        
        
      
    -   Interfaces help reduce duplication, that is, the amount of boilerplate code. 
 -   They make it easier to use stubs in unit tests instead of real objects. 
 -   Being an architectural tool, interfaces help untie parts of your code base. 
 
      
        
        
        
      
      Let's take a closer look at these ways of using interfaces. 
      
        
        
        
      
    
      
        
        
        
      
      Reduce the amount of boilerplate code 
      
        
        
        
      
      Suppose we have a 
Customer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     structure containing some kind of customer data.  In one part of the code, we want to write this information to 
bytes.Buffer , and in the other part we want to write client data to 
os.File on disk.  But, in both cases, we want to first serialize the 
ustomer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     structure to JSON. 
      
        
        
        
      
    
      
        
        
        
      
      In this scenario, we can reduce the amount of boilerplate code using Go interfaces. 
      
        
        
        
      
    
      
        
        
        
      
      Go has an 
io.Writer interface type: 
      
        
        
        
      
    
      
        
        
        
      
     type Writer interface { Write(p []byte) (n int, err error) }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      And we can take advantage of the fact that 
bytes.Buffer and the 
os.File type satisfy this interface, because they have the 
bytes.Buffer.Write () and 
os.File.Write () methods, respectively. 
      
        
        
        
      
    
      
        
        
        
      
      Simple implementation: 
      
        
        
        
      
    
      
        
        
        
      
     package main import ( "encoding/json" "io" "log" "os" ) 
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Of course, this is just a fictitious example (we can structure the code differently to achieve the same result).  But it illustrates well the advantages of using interfaces: we can create the 
Customer.WriteJSON()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     method once and call it every time we need to write to something that satisfies the 
io.Writer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     interface. 
      
        
        
        
      
    
      
        
        
        
      
      But if you are new to Go, you will have a couple of questions: “ 
How do I know if the io.Writer interface exists at all?  And how do you know in advance that he is satisfied bytes.Buffer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     and os.File
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     ?  " 
      
        
        
        
      
    
      
        
        
        
      
      I'm afraid there is no simple solution.  You just need to gain experience, get acquainted with the interfaces and different types from the standard library.  This will help reading the documentation for this library and viewing someone else's code.  And for quick reference, I added the most useful types of interface types to the end of the article. 
      
        
        
        
      
    
      
        
        
        
      
      But even if you do not use interfaces from the standard library, nothing prevents you from creating and using 
your own interface types .  We will talk about this below. 
      
        
        
        
      
    
      
        
        
        
      
      Unit Testing and Stubs 
      
        
        
        
      
      To understand how interfaces help in unit testing, let's look at a more complex example. 
      
        
        
        
      
    
      
        
        
        
      
      Suppose you have a store and store information about sales and the number of customers in PostgreSQL.  You want to write a code that calculates the share of sales (specific number of sales per customer) for the last day, rounded to two decimal places. 
      
        
        
        
      
    
      
        
        
        
      
      A minimal implementation would look like this: 
      
        
        
        
      
    
      
        
        
        
      
     
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Now we want to create a unit test for the 
calculateSalesRate()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     function to verify that the calculations are correct. 
      
        
        
        
      
    
      
        
        
        
      
      Now this is problematic.  We will need to configure a test instance of PostgreSQL, as well as create and delete scripts to populate the database with fake data.  We will have to do a lot of work if we really want to test our calculations. 
      
        
        
        
      
    
      
        
        
        
      
      And the interfaces come to the rescue! 
      
        
        
        
      
    
      
        
        
        
      
      We will create our own interface type that describes the 
CountSales()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     and 
CountCustomers()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     methods, which the 
calculateSalesRate()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     function relies on.  Then update the signature 
calculateSalesRate()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     to use this interface type as a parameter instead of the prescribed 
*ShopDB
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     type. 
      
        
        
        
      
    
      
        
        
        
      
      Like this: 
      
        
        
        
      
    
      
        
        
        
      
     
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      After we have done this, it will be easy for us to create a stub that satisfies the 
ShopModel
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     interface.  Then you can use it during unit testing of the correct operation of mathematical logic in the function 
calculateSalesRate()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     .  Like this: 
      
        
        
        
      
    
      
        
        
        
      
     
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Now run the test and everything works fine. 
      
        
        
        
      
    
      
        
        
        
      
      Application architecture 
      
        
        
        
      
      In the previous example, we saw how you can use interfaces to decouple certain parts of the code from using specific types.  For example, the 
calculateSalesRate()
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     function does not matter what you pass to it, as long as it satisfies the 
ShopModel
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     interface. 
      
        
        
        
      
    
      
        
        
        
      
      You can expand this idea and create whole “untied” levels in large projects. 
      
        
        
        
      
      Suppose you are creating a web application that interacts with a database.  If you make an interface that describes certain methods for interacting with the database, you can refer to it instead of a specific type through HTTP handlers.  Since HTTP handlers refer only to the interface, this will help to decouple the HTTP level and the level of interaction with the database from each other.  It will be easier to work with levels independently, and in the future you will be able to replace some levels without affecting the work of others. 
      
        
        
        
      
    
      
        
        
        
      
      I wrote about this pattern in 
one of the previous posts , there are more details and practical examples. 
      
        
        
        
      
    
      
        
        
        
      
      What is an empty interface? 
      
        
        
        
      
      If you have been programming on Go for some time, then you have probably come across an 
empty interface type interface{}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     .  I’ll try to explain what it is.  At the beginning of this article, I wrote: 
      
        
        
        
      
    
      
        
        
        
      
      The interface type in Go is a kind of definition .  It defines and describes the specific methods that some other type should have . 
      
        
        
        
      
      An empty interface type 
does not describe methods .  He has no rules.  And so any object satisfies an empty interface. 
      
        
        
        
      
    
      
        
        
        
      
      In essence, the empty interface type 
interface{}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     is a kind of joker.  If you meet it in a declaration (variable, function parameter or structure field), then you can use an object of 
any type . 
      
        
        
        
      
    
      
        
        
        
      
      Consider the code: 
      
        
        
        
      
    
      
        
        
        
      
     package main import "fmt" func main() { person := make(map[string]interface{}, 0) person["name"] = "Alice" person["age"] = 21 person["height"] = 167.64 fmt.Printf("%+v", person) }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Here we initialize the 
person
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     map, which uses a string type for keys, and an empty interface type 
interface{}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     for values.  We assigned three different types as map values (string, integer and float32), and no problem.  Since objects of any type satisfy the empty interface, the code works great. 
      
        
        
        
      
    
      
        
        
        
      
      You can 
run this code here , you will see a similar result: 
      
        
        
        
      
    
      
        
        
        
      
     map[age:21 height:167.64 name:Alice]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      When it comes to extracting and using values from a map, it’s important to keep this in mind.  Suppose you want to get the 
age
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     value and increase it by 1. If you write a similar code, then it will not compile: 
      
        
        
        
      
    
      
        
        
        
      
     package main import "log" func main() { person := make(map[string]interface{}, 0) person["name"] = "Alice" person["age"] = 21 person["height"] = 167.64 person["age"] = person["age"] + 1 fmt.Printf("%+v", person) }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      You will receive an error message: 
      
        
        
        
      
    
      
        
        
        
      
     invalid operation: person["age"] + 1 (mismatched types interface {} and int)
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      The reason is that the value stored in map takes the type 
interface{}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     and loses its original, base int type.  And since the value is no longer integer, we cannot add 1 to it. 
      
        
        
        
      
    
      
        
        
        
      
      To get around this, you need to make the value integer again, and only then use it: 
      
        
        
        
      
    
      
        
        
        
      
     package main import "log" func main() { person := make(map[string]interface{}, 0) person["name"] = "Alice" person["age"] = 21 person["height"] = 167.64 age, ok := person["age"].(int) if !ok { log.Fatal("could not assert value to int") return } person["age"] = age + 1 log.Printf("%+v", person) }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      If you 
run this , everything will work as expected: 
      
        
        
        
      
    
      
        
        
        
      
     2009/11/10 23:00:00 map[age:22 height:167.64 name:Alice]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      So when should you use an empty interface type? 
      
        
        
        
      
    
      
        
        
        
      
      Perhaps 
not too often .  If you come to this, then stop and think about whether it is right to use 
interface{}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     .  As a general advice, I can say that it will be more understandable, safer and more productive to use specific types, that is, non-empty interface types.  In the above example, it was better to define a 
Person
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     structure with appropriately typed fields: 
      
        
        
        
      
    
      
        
        
        
      
     type Person struct { Name string Age int Height float32 }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      An empty interface, on the other hand, is useful when you need to access and work with unpredictable or user-defined types.  For some reason, such interfaces are used in different places in the standard library, for example, in the 
gob.Encode , 
fmt.Print, and 
template.Execute functions. 
      
        
        
        
      
    
      
        
        
        
      
      Useful Interface Types 
      
        
        
        
      
      Here is a short list of the most requested and useful interface types from the standard library.  If you are not already familiar with them, then I recommend reading the relevant documentation. 
      
        
        
        
      
    
      
        
        
        
      
    
      
        
        
        
      
      A longer list of standard libraries is also available 
here .