Go Interfaces
An interface defines a set of method signatures. Any type that implements all those methods automatically satisfies the interface — no explicit declaration needed. Interfaces make it possible to write code that works with different types through a common contract.
Defining and Implementing an Interface
package main
import "fmt"
// Interface definition
type Shape interface {
Area() float64
Perimeter() float64
}
// Circle implements Shape
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * 3.14159 * c.Radius
}
// Rectangle implements Shape
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
func printShape(s Shape) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}
func main() {
c := Circle{Radius: 5}
r := Rectangle{Width: 4, Height: 6}
printShape(c) // Area: 78.54, Perimeter: 31.42
printShape(r) // Area: 24.00, Perimeter: 20.00
}
Interface Satisfaction Diagram
Shape Interface:
┌──────────────────────────┐
│ Area() float64 │
│ Perimeter() float64 │
└──────────────────────────┘
▲ ▲
│ │
Circle Rectangle
has both has both
methods ─── methods
→ satisfies → satisfies
Shape Shape
Go checks interface satisfaction at compile time. If a type is missing any required method, the compiler reports an error immediately.
Implicit Implementation
There is no implements keyword in Go. A type satisfies an interface simply by having all the required methods. This is called implicit implementation.
// No need to write: type Circle implements Shape
// Go figures it out automatically
Interface with a Single Method
Interfaces with one method are very common in Go. By convention, the interface name matches the method name plus an "er" suffix.
package main
import "fmt"
type Stringer interface {
String() string
}
type Dog struct {
Name string
Breed string
}
func (d Dog) String() string {
return fmt.Sprintf("%s (%s)", d.Name, d.Breed)
}
func describe(s Stringer) {
fmt.Println(s.String())
}
func main() {
d := Dog{Name: "Bruno", Breed: "Labrador"}
describe(d) // Bruno (Labrador)
}
The Empty Interface
An interface with no methods is satisfied by every type. It can hold a value of any type.
package main
import "fmt"
func printAnything(val interface{}) {
fmt.Println(val)
}
func main() {
printAnything(42)
printAnything("hello")
printAnything(true)
printAnything([]int{1, 2, 3})
}
Output:
42
hello
true
[1 2 3]
In modern Go (1.18+), any is an alias for interface{} and is preferred.
func printAnything(val any) {
fmt.Println(val)
}
Interface Slice – Polymorphism
A slice of an interface type holds different concrete types together.
package main
import "fmt"
type Animal interface {
Sound() string
}
type Dog struct{}
type Cat struct{}
func (d Dog) Sound() string { return "Woof" }
func (c Cat) Sound() string { return "Meow" }
func main() {
animals := []Animal{Dog{}, Cat{}, Dog{}}
for _, a := range animals {
fmt.Println(a.Sound())
}
}
Output:
Woof
Meow
Woof
Key Points
- An interface defines method signatures — any type with those methods satisfies it automatically
- No explicit declaration is needed — satisfaction is implicit
- Functions accepting an interface work with any type that satisfies it
- The empty interface (
interface{}orany) accepts any type - Interfaces enable polymorphism — different types behaving through a common contract
