Hello! Welcome to the second module of our course.
In our last lesson, we delved into the design philosophy behind Go's interfaces, contrasting its implicit satisfaction with the explicit implementation common in other languages. We established that the core rationale is to enable true decoupling, allowing interfaces to be defined by the consumer of a type, not its producer.
Now that we understand the "why," we'll focus on the "how." This lesson moves from theory to practice. We will explore the mechanics of Go's interface system, covering two key aspects:
- How to define an interface using Go's syntax.
- How implicit satisfaction works mechanically when you assign a concrete type's value to an interface type.
By the end of this lesson, you'll be able to define a Go interface and explain the mechanics of how different concrete types can satisfy it and be used interchangeably through an interface variable.
1. Defining an Interface: The Contract
In Go, an interface is a type that specifies a set of method signatures. It acts as a contract. Any other type that has all the methods listed in the contract is said to "satisfy" or "implement" the interface.
The syntax is straightforward. You use the type keyword, followed by the interface's name, the interface keyword, and a block of method signatures.

A method signature defines the "shape" of a method—what it's called, what it accepts, and what it returns. The interface doesn't care how the method is implemented, only that a method with that exact signature exists.
Let's look at a practical example from the following video. It introduces a DBContract interface, which defines the behavior expected from any database type that wants to work with a specific application.
In this clip from 'Master Golang with Interfaces' by Kantan Coding, we'll see how a Go interface is defined to serve as a contract between an application and its database component.
Please watch from 09:35 to 10:25. Focus on the DBContract interface definition. Notice how it's just a list of method signatures, like Close() and InsertUser(...), which sets expectations for any type that will be used as a database.
This DBContract is a perfect example of an interface defining a set of required behaviors without specifying any implementation details.
2. The Core Mechanic: Implicit Satisfaction and Assignment
Now we get to the heart of the matter. We have a contract (the interface), but how does a concrete type (like a struct) fulfill it?
As we discussed in the last lesson, this happens implicitly. You don't use a keyword like implements. You simply define methods on your concrete type that match the signatures in the interface. If your type has all the required methods, the Go compiler understands that it satisfies the interface.
This enables the key mechanic: you can assign a value of a concrete type to a variable of an interface type it satisfies.
Let's see this entire process play out. The next video segment walks through:
- Defining methods on a
MySQLDBstruct that match theDBContractinterface. - Creating a function that accepts the
DBContractinterface as a parameter. - Passing a
MySQLDBinstance to that function, demonstrating the assignment of a concrete type to an interface type. - Showing the compile-time error that occurs if you try to pass a type that doesn't satisfy the contract.
Let's continue with the 'Master Golang with Interfaces' video. This is a crucial section that demonstrates the entire mechanic of implicit satisfaction from start to finish.
Watch from 12:40 to 16:58. Pay close attention to these three parts: The method definitions for MySQLDB (e.g., func (db *MySQLDB) Close() ...). This is how the struct fulfills the contract. The newApplication function signature, which accepts the interface DBContract. The call to newApplication(db), where the concrete MySQLDB type is assigned to the interface type. This is the central mechanic in action.
This example clearly shows the flow: a function is written to depend on an abstract behavior (DBContract), and any concrete type that provides that behavior (MySQLDB) can be used with it. The compiler acts as the verifier, ensuring the contract is met before the code even runs.
3. A Standard Library Example: io.Writer
This mechanic of assigning concrete types to interface variables is not just a niche feature; it's fundamental to the Go standard library. One of the most ubiquitous examples is the io.Writer interface. It is defined as:
type Writer interface {
Write(p []byte) (n int, err error)
}
It's a small interface with a single method, Write. Countless types in Go satisfy this interface:
- An open file on disk (
*os.File) - A network connection (
net.Conn) - An in-memory buffer (
bytes.Buffer) - The standard output to your console (
os.Stdout)
This allows us to write a single, generic function that can write data to any of these destinations, simply by accepting an io.Writer.
The following video demonstrates this beautifully, showing the same save function writing data first to a file and then to a network connection, all thanks to the io.Writer interface.
What Makes Golang Go: The Power of Go Interfaces — Ricardo Gerardi
In this clip from 'What Makes Golang Go' by Ricardo Gerardi, you'll see the power of the io.Writer interface. It's a classic demonstration of how different concrete types can be used interchangeably through a common interface.
Watch from 22:03 to 25:05. Observe how the save function takes an io.Writer. First, it's given a file handle, and then it's given a network connection. The save function's code doesn't change because both types satisfy the Writer contract.
This flexibility is a direct result of the mechanic we're studying. A function asks for a behavior (the ability to write), and it can accept any concrete type that provides that behavior.
4. A Note on "Structured Duck Typing"
Given your experience with Python, this pattern may remind you of "duck typing" ("If it walks like a duck and it quacks like a duck, then it must be a duck"). Go's approach is a variation on this theme, often called structured duck typing.
You get the flexibility of using any type that has the right behavior, but with the crucial addition of compile-time safety. Unlike in a dynamic language like Python where a missing method would cause a runtime error, the Go compiler verifies that the "duck" can indeed "quack" before compiling your program.
This short clip provides a great summary of this idea.
What Makes Golang Go: The Power of Go Interfaces — Ricardo Gerardi
Let's watch one more brief clip from 'What Makes Golang Go' that connects Go's interfaces to the concept of duck typing.
Watch from 29:46 to 31:08. The key takeaway is the distinction made: in Go, it's not that the value is a duck, but that it behaves like a duck, and this behavior is guaranteed by the compiler.
This provides the best of both worlds: the flexibility of duck typing combined with the robustness of static type checking.
Conclusion
Today, we've moved from the philosophy of Go's interfaces to their practical mechanics. We've seen how they are defined and, most importantly, how the mechanism of implicit satisfaction allows concrete types to be assigned to and used through interface types.
Key Takeaways:
- Interface Definition: An interface is defined with the
typeandinterfacekeywords and contains a set of method signatures. - Implicit Satisfaction: A type satisfies an interface automatically if it implements all the methods declared in that interface's method set. No
implementskeyword is necessary. - Assignment Mechanic: A value of a concrete type can be assigned to a variable of an interface type it satisfies. This is the core of polymorphism in Go.
- Compile-Time Verification: The Go compiler checks for interface satisfaction, ensuring type safety before the program runs.
Preview of the Next Lesson
Now that we understand both the "why" and the "how," we are ready to discuss idiomatic Go. In our final lesson, we will explore common design patterns that emerge from this system, including the "accept interfaces, return structs" principle and the powerful role of small, single-method interfaces.