As I was learning Go , I figured out there were 3 types of interfaces :
 
-Built-in interfaces.
These are interfaces provided bu the Go standard library, being universally
available in any Go environment. Typical examples include ‘io.Reader’ , ‘io.Writer’ and ‘error’.
 

-Interfaces defined in external packages.
These interfaces are defined in third-party librairies or packages.
They are not part of the go standard library and can be imported and used in Go programs.
 
-Interfaces defined by you -the Go developper.
These user-defined interfaces allow you to create custom abstractions relevant to your applications' specific needs.
 

Interfaces are about expressing abstractions , identifying and defining behaviors that can be shared among different data types. Once you have coded an interface for a data type, a new universe of functionality becomes available to the variables and the values of that type, a way for you to be more productive.

Interfaces in Go language are viewed as virtual data types or abstract types without their own values.
An interface is 2 things -> a set of methods & a type .

In other words, an interface is a Go mechanism for defining behavior that is implemented using a set of methods.
Interfaces play a key role in Go and simplify the code of your programs when they have to deal with multiple data types
performing the same task.
 

If you code your own custom interfaces, small and well-defined interfaces are the way to go and, as a role of thumb,
it is best pratice to create a new interface when you want to share a common behavior between one or more concrete data type ;
such approach being similar to ‘duck typing’ found in dynamic programming languages.
 

A great example of using interfaces is that you can revisit the ‘error’ data type in your Go codebase by defining it
by an interface as such :  

type error interface {
  Error() string
}

 
In doing so, you can give more context to an error condition and choose not to rely on the default error type.
In the below example, the ‘MyError’ struct implements the ‘Error’ interface by defining the ‘Error() string’ method.
We can then create an instance of ‘MyError’ and use it like any other error, with the added benefit of your custom error message format:
 

package main

import (
  "fmt"
)
// Error is an interface with a single method , Error(), returning a string.
type Error interface { 
  Error() string
}
// My Error is a custom error type including extra information.
type MyError struct { 
  Msg string
  Code int
}

// Error() implements the error interface for MyError.
// This method formats the error message including the code and messge.
func (e MyError) Error() string { 
  return fmt.Sprintf("Code: %d, Msg: %s", e.Code, e.Msg) 
}

func main() { 
  // Example usage of 'MyError' 
  err := MyError{"something went wrong", 404}
  fmt.Prinln(err.Error()) 
} 

 

The output will print : ‘Code: 404, Msg: something went wrong’