
Golang interfaces are a powerful tool for achieving polymorphism, allowing you to write code that works with multiple types.
In Golang, an interface is a collection of method signatures. This means that any type that implements all the methods in an interface can be treated as if it implements the interface itself.
A key aspect of Golang interfaces is that they are implicitly implemented by any type that has the required methods, without the need for an explicit interface implementation.
By leveraging Golang interfaces, you can write more flexible and reusable code that adapts to different types and scenarios.
A unique perspective: Golang Comments
What is an Interface?
An interface in Go is a definition that describes the exact methods a type must have.
It's a way to define what a type must be able to do, rather than how it does it.
The fmt.Stringer interface is an example of an interface type from the standard library, which requires a String() string method.
Any type that has this method satisfies the interface.
For example, the Book type has a String() string method, so it satisfies the fmt.Stringer interface.
Similarly, the Count type also satisfies the fmt.Stringer interface because it has a String() string method.
The key thing to note is that two different types, Book and Count, can satisfy the same interface.
This means that if you know an object satisfies the fmt.Stringer interface, you can rely on it having a String() string method that you can call.
In Go, when you declare a variable, function parameter, or struct field with an interface type, you can use any object that satisfies the interface.
For example, the WriteLog() function uses the fmt.Stringer interface type in its parameter declaration, so you can pass in any object that satisfies the interface.
This makes the function flexible and agnostic about the exact type of object it receives.
Consider reading: Golang Function Type
Why Use Interfaces?
Interfaces in Go are incredibly useful, and I've found that they're often used in three key ways.
One of the main reasons to use interfaces is to reduce duplication or boilerplate code. This is especially true when you have multiple objects that need to implement the same functionality.
By using interfaces, you can define a contract that must be implemented by any type that conforms to it, without having to duplicate code. This makes your code more maintainable and easier to read.
Here are the three most common use-cases for interfaces in Go:
- To help reduce duplication or boilerplate code.
- To make it easier to use mocks instead of real objects in unit tests.
- As an architectural tool, to help enforce decoupling between parts of your codebase.
Interface Basics
In Go, interfaces are defined by a set of method signatures. Any type that implements all the methods of an interface satisfies that interface without needing an "implements" keyword.
Go's approach to interfaces is different from many other languages, where interfaces are typically defined alongside their implementations. In Go, it's preferred to define interfaces in the package that uses them, rather than in the package that implements them.
This approach promotes loose coupling and makes code more maintainable and flexible. For example, instead of defining an interface in a separate package, you can define it directly in the package that needs it.
Additional reading: Golang Go
Basics

Interfaces in Go are defined by a set of method signatures, and any type that implements all the methods of an interface satisfies that interface without needing an "implements" keyword.
In Go, you can define interfaces in the package that uses them, rather than in the package that implements them. This is different from other languages where interfaces are typically defined alongside their implementations.
A simple example of this is defining a Geometry interface with Area() and Perimeter() methods, which can be implemented by different types like Rectangle and Circle.
To enable polymorphism without inheritance hierarchies, Go's interfaces allow you to define a set of methods that can be implemented by different types.
For example, you can define a Geometry interface with Area() and Perimeter() methods, and then implement it by different types like Rectangle and Circle.
Here are some key benefits of using interfaces in Go:
- Mocking: You can pass an implementation of an interface for testing purposes.
- Scaffolding: You can write code for functionality you have yet to implement but understand which variables or functionality you need.
- Volatility: You can switch your database by adding a new implementation of an interface and keeping the code that relies on this “database” interface.
- Interoperability: You can make different aspects of your code base compatible by having it communicate with each other through interfaces.
Interfaces can also be used to reduce boilerplate code by defining a common interface that can be implemented by different types.

For example, you can define an io.Writer interface with a Write() method, and then use it to write to different types like bytes.Buffer and os.File.
This is just a toy example, but it nicely illustrates the benefit of using an interface – you can create a method once and call it any time you want to write to something that satisfies the io.Writer interface.
Here are some of the most useful interface types in the standard library:
By using interfaces in Go, you can write more maintainable, flexible, and maintainable code that is easier to understand and work with.
Type Assertion
Type Assertion is a way to extract the underlying value of an interface. It's used to access the concrete type behind an interface.
The syntax for Type Assertion is i.(T), where i is the interface and T is the type you're trying to access. For example, in the program above, i.(int) is used to fetch the underlying int value of i.
For more insights, see: T Golang
Type Assertion will panic with a message like "interface conversion: interface {} is string, not int" if the concrete type of the interface is not the type you're trying to access.
To avoid panicking, you can use the syntax "if i.(T) != nil" to check if the type is correct before trying to access it. However, this is not shown in the examples.
In some cases, you might not know what type you're dealing with, and that's where Type Assertion comes in. It allows you to extract the underlying type and then check its value.
The zero value of a type will be returned if the Type Assertion fails. For example, if you try to access an int value from a string, the zero value of int (0) will be returned.
Type Assertion is a powerful tool for working with interfaces, but it requires careful use to avoid panicking.
Related reading: Golang Check Type
Embedding
Embedding is a powerful way to compose interfaces in Go, allowing you to create more complex interfaces by combining simpler ones.
In Go, you can embed interfaces within other interfaces, which is called interface embedding. This means you can create a new interface that includes all the methods of the embedded interfaces.
The PrinterScanner interface is an example of interface embedding, as it embeds both the Printer and Scanner interfaces. This means any type that satisfies the PrinterScanner interface must implement both the Print() and Scan() methods.
By embedding interfaces, you can create more specific and focused interfaces that are easier to work with. This makes your code more organized and maintainable.
A struct can satisfy an embedded interface by implementing all the methods of the embedded interfaces. For example, the PrinterScannerImpl struct satisfies the PrinterScanner interface because it implements both the Print() and Scan() methods.
Embedding interfaces also allows you to create functions that can work with any type that satisfies the embedded interface. For example, the PrintAndScan() function can be used with any type that satisfies the PrinterScanner interface.
Curious to learn more? Check out: Golang Copy Struct
Internal Representation
An interface is internally represented as a tuple containing two elements: its type and value.
The type element specifies the underlying concrete type that implements the interface.
In the case of the Worker interface, its type is determined by the Person struct that implements it.
The value element holds the value of the concrete type.
For instance, when a variable of type Person is assigned to a variable of type Worker, the concrete type of the interface changes to Person.
This is evident in the program where the variable p of type Person is assigned to w, which is of type Worker.
The describe function prints the value and concrete type of the interface, showing that the interface now contains a Person with a name field.
Declaring and Implementing Interfaces
Declaring and implementing an interface in Go is straightforward. You simply declare the interface with the methods it should contain, and any type that wants to satisfy this interface must implement all of those methods.
To declare an interface, you can simply define a type with the methods it should contain. For example, the SalaryCalculator interface is declared with a single method CalculateSalary() int.
Any type that wants to implement an interface must contain all the methods declared in the interface. This is different from other languages like Java, where a class has to explicitly state that it implements an interface using the implements keyword.
In Go, you can implement an interface by adding the required methods to the receiver type. For example, the Permanent struct implements the SalaryCalculator interface by adding the CalculateSalary() int method.
You can also implement an interface with a pointer receiver. For example, the CalculatorImpl struct satisfies the AdvancedCalculator interface with a pointer receiver.
One of the biggest advantages of interfaces in Go is that they can be extended to any new type without any code changes. This is demonstrated by the totalExpense function, which takes a slice of SalaryCalculator interface as a parameter and can calculate the expense for any type that implements the SalaryCalculator interface.
The totalExpense function can be extended to any new type without any code changes, as long as that type implements the SalaryCalculator interface. This is shown by adding the Freelancer struct, which implements the SalaryCalculator interface and can be passed to the totalExpense function without any code changes.
Discover more: Go vs Golang
Interface Types and Switches
Interface types are a fundamental concept in Go, and understanding them is crucial for working with interfaces. The standard library provides a variety of useful interface types, including builtin.Error, fmt.Stringer, io.Reader, io.Writer, io.ReadWriteCloser, http.ResponseWriter, and http.Handler.
Here are some of the most common and useful interfaces in the standard library:
- builtin.Error
- fmt.Stringer
- io.Reader
- io.Writer
- io.ReadWriteCloser
- http.ResponseWriter
- http.Handler
Type assertions and type switches are used to access the underlying concrete type of an interface value. A type switch is similar to a switch statement, but it compares the concrete type of an interface against multiple types specified in case statements.
Type Assertions and Switches
Type assertions are used to extract the underlying value of an interface, and the syntax i.(T) is used to get the underlying value of interface i whose concrete type is T.
Type assertions can panic if the concrete type is not what we expect, as seen in the example where the program tries to extract an int value from a string, resulting in a panic message.
The problem of type assertions panicking can be solved by using the syntax 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.
Type switches are used to compare the concrete type of an interface against multiple types specified in various case statements, similar to a switch case.
The syntax for type switches is similar to type assertions, with the type T replaced by the keyword type, as seen in the example switch i.(type).
Type switches can compare a type to an interface if the type implements the interface, as seen in the example where the Person struct implements the Describer interface.
In a type switch, if a case matches, the corresponding statement is printed, as seen in the example where the output is 89.98 in line no. 20 is of type float64.
For your interest: Golang Ok
Common Types
Common Types are a crucial part of Go programming. They are interfaces that are built into the standard library, making them super useful and widely applicable.
The standard library includes a range of common and useful interfaces, such as builtin.Error and fmt.Stringer. These interfaces are designed to be flexible and adaptable, allowing developers to write more efficient and effective code.
Some of the most common and useful interfaces in the standard library include:
- builtin.Error
- fmt.Stringer
- io.Reader
- io.Writer
- io.ReadWriteCloser
- http.ResponseWriter
- http.Handler
These interfaces are all part of the standard library, making them easy to use and understand. They're a great place to start when building new applications or working with existing code.
Common Interface Patterns
Common Interface Patterns are a crucial part of Go's interface. They allow you to write more flexible and reusable code.
The standard library provides a range of common interfaces that are useful to know about. These include builtin.Error, fmt.Stringer, io.Reader, io.Writer, io.ReadWriteCloser, http.ResponseWriter, and http.Handler.
Explore further: Golang Io
These interfaces can be used to implement common patterns such as the Strategy Pattern and the Observer Pattern. The Strategy Pattern defines a family of algorithms and makes them interchangeable. The Observer Pattern allows an object to notify other objects of state changes.
Here are some common interfaces you should know about:
- builtin.Error
- fmt.Stringer
- io.Reader
- io.Writer
- io.ReadWriteCloser
- http.ResponseWriter
- http.Handler
Error Handling and Testing
Implementing interfaces allows for flexible and reusable code, as seen in Listing 4 where the same variable is reused to turn on both a laptop and a washing machine.
This flexibility is also useful for error handling, as Go's error interface is a prime example of interfaces in action. Any type that implements an Error() method satisfies this interface.
By following this simple design, you can create rich error handling with detailed information while still adhering to the error interface contract, as demonstrated in the example of error handling with interfaces.
Worth a look: Golang Create Error
Error Handling
Error handling is a crucial aspect of programming, and Go's error interface is a key part of it.
Any type that implements an Error() method satisfies the error interface, allowing for rich error handling.
This simple design allows for detailed error information while still adhering to the error interface contract.
Go's error interface is one of the most common applications of interfaces in the language, making it a powerful tool for programmers.
Error handling with interfaces is a pattern that allows for detailed error information, making it easier to diagnose and fix issues.
This pattern is a key part of Go's error handling capabilities, and it's a must-know for any programmer working with the language.
Testing the Code
Testing with interfaces makes it easy to mock dependencies, allowing you to avoid actual databases and focus on the code.
By defining a Database interface, you can substitute a mock implementation for testing, applying the dependency inversion principle.
Unit testing becomes much simpler with interfaces, as you can create a mock that satisfies the interface and test the code without setting up a test database.
Readers also liked: Golang Source
This approach makes it straightforward to test the math logic in functions like calculateSalesRate() without the hassle of setting up a test database.
You can create a mock that satisfies the ShopModel interface and use it during unit tests to ensure the math logic works correctly.
Reusing the same variable for different types of appliances, like a Laptop and a WashingMachine, is possible because they both implement and satisfy the Appliance interface.
Interfaces shine in testing by enabling easy mocking of dependencies, making it easier to write unit tests and catch errors early in the development process.
Best Practices and Common Pitfalls
As you work with Go interfaces, you'll want to keep these best practices in mind.
Define interfaces with just the methods you need, and often at the point where they're used, not where types are defined. This will help keep your code clean and maintainable.
A key thing to remember is that interface values are value types, not reference types. This means that when you pass an interface value around, you're actually copying the entire interface, including the underlying value.
Not using pointer receivers consistently can lead to confusion. If some methods on a type use pointer receivers, others should too.
Here are some common pitfalls to watch out for:
- Overusing interfaces can lead to weak abstractions.
- Not understanding the difference between a nil interface and an interface containing a nil pointer can cause problems.
- Embedding types with conflicts can result in ambiguous code.
To avoid these pitfalls, make sure to use interfaces judiciously and understand the implications of using value types versus reference types.
Real-World Applications and Examples
Interfaces are a powerful tool in Go, and they have many practical uses in real-world applications.
The http.Handler interface is used in the Go standard library's net/http package to define a handler for HTTP requests. Any type that implements the ServeHTTP() method with the correct signature can be used as an http.Handler.
You can use interfaces to define custom sorting for types, such as the sort.Interface interface used in the Go standard library's sort package. This interface defines the Len(), Less(), and Swap() methods that are used to sort a slice of Person structs by their age.
For your interest: Install Golang Package
The io.Reader and io.Writer interfaces are used in the Go standard library's io package to define input and output streams. Any type that implements the Read() or Write() method with the correct signature can be used as an io.Reader or io.Writer.
Interfaces can be used to define database drivers, such as the database/sql/driver package used in the Go standard library's database/sql package. The driver.Driver interface defines a method called Open() that returns a new driver.Conn interface.
Here are some examples of how interfaces are used in popular Go packages:
You can use interfaces to make different aspects of your code base compatible, such as communicating with each other through interfaces. This characteristic can be better expressed if you're developing a library and plan on using it with two or more projects.
Frequently Asked Questions
Can an interface be a type?
Yes, an interface can be considered a type, as it defines a new reference data type that can be used anywhere a data type name is required. This allows for polymorphism and flexibility in programming.
When to use type over interface?
Use types by default, as they can express complex types like unions, mapped types, and conditional types, whereas interfaces are best suited for simple type definitions
Featured Images: pexels.com


