Interfaces and Duck Typing
In Go, an Interface is a set of method signatures. Unlike languages like Java or C#, where a class must explicitly declare that it "implements" an interface, Go uses Implicit Implementation.
If a type defines all the methods required by an interface, it satisfies that interface automatically.
1. Defining and Using Interfaces
An interface is a contract. It doesn't care what a Type is; it only cares what it can do.
type Writer interface {
Write([]byte) (int, error)
}
func SaveDocument(w Writer, data []byte) {
w.Write(data)
}
- Polymorphism: The
SaveDocumentfunction can accept aFile, aNetworkConn, or aBuffer—as long as they have aWritemethod.
2. Interface Internals: iface and eface
Under the hood, an interface is a Two-Word Data Structure (8 bytes each on a 64-bit system).
- The Type Word: Points to information about the underlying concrete type (its name, methods, etc.).
- The Data Word: A pointer to the actual data (the struct or value).
iface: Used for interfaces with methods.eface: Used for the empty interface (interface{}orany), which has no methods and can hold any type.
3. The Empty Interface (any)
Introduced in Go 1.18, any is an alias for interface{}. Use this when you need a function to accept absolutely any type. However, because you lose type safety, you must use Type Assertions or Type Switches to get the actual value back.
func HandleAnything(val any) {
switch v := val.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Int:", v)
}
}
4. Why Implicit Implementation is Powerful?
- Decoupling: You can define an interface in the "Consuming" package rather than the "Providing" package. You don't need permission from the original library author to mock their types.
- Mocking: Testing becomes significantly easier. You can swap a real database for a "Mock" struct that satisfies the same interface.
Interview Pro-Tips: Why is interface{} not the same as nil?
A common "Gotcha": An interface variable is only nil if both its type and its data are nil.
If you store a nil pointer to a struct inside an interface, the interface itself is NOT nil because its "Type" word is populated. This can lead to unexpected behavior in if val != nil checks.
Technical Summary
Implicit: Satisfied by behavior, not declaration.Any: The universal container (use sparingly).Contract: Interfaces allow you to write generic code that works across multiple unrelated types.