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{} or any) accepts any type
  • Interfaces enable polymorphism — different types behaving through a common contract

Leave a Comment