Interface

Interfaces are a special type that define a collection of function signatures. You can think of an interface as saying, "a type must implement function X and function Y to satisfy this interface." If you create any type and implement the functions needed to satisfy the interface, your type can be used anywhere that the interface is expected. You don't have to specify that you are trying to satisfy an interface, the compiler will determine if it satisfies the requirements.

You can add as many other functions as you want to your custom type. The interface defines the functions that are required, but it does not mean that your type is limited to implementing only those functions.

The most commonly used interface is the error interface. The error interface only requires a single function to be implemented, a function named Error() that returns a string with the error message. Here is the interface definition:

type error interface {
Error() string
}

This makes it very easy for you to implement your own error interfaces. This example creates a customError type and then implements the Error() function needed to satisfy the interface. Then, a sample function is created, which returns the custom error:

package main

import "fmt"

// Define a custom type that will
// be used to satisfy the error interface
type customError struct {
Message string
}

// Satisfy the error interface
// by implementing the Error() function
// which returns a string
func (e *customError) Error() string {
return e.Message
}

// Sample function to demonstrate
// how to use the custom error
func testFunction() error {
if true != false { // Mimic an error condition
return &customError{"Something went wrong."}
}
return nil
}

func main() {
err := testFunction()
if err != nil {
fmt.Println(err)
}
}

Other frequently used interfaces are the Reader and Writer interfaces. Each one only requires one function to be implemented in order to satisfy the interface requirements. The big benefit here is that you can create your own custom types that reads and writes data in some arbitrary way. The implementation details are not important to the interface. The interface won't care whether you are reading and writing to a hard disk, a network connection, storage in memory, or /dev/null. As long as you implement the function signatures that are required, you can use your type anywhere the interface is used. Here is the definition of the Reader and Writer interfaces:

type Reader interface {
Read(p []byte) (n int, err error)
} type Writer interface {
Write(p []byte) (n int, err error)
}