Go Interfaces

Defining contracts for behavior in Go

🔌 What are Go Interfaces?

Go interfaces define method signatures that types must implement. They enable polymorphism and loose coupling, allowing different types to be used interchangeably when they satisfy the same interface contract.


// Define an interface
type Speaker interface {
    Speak() string
}

// Types that implement Speaker
type Dog struct{}
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{}
func (c Cat) Speak() string { return "Meow!" }
                                    

Output:

Woof!
Meow!

Key Interface Concepts

📋

Contract

Defines what methods a type must have

type Writer interface {
    Write([]byte) (int, error)
}
🔄

Implicit Implementation

No explicit "implements" keyword needed

// Automatically implements Writer
type MyWriter struct{}
🎭

Polymorphism

Different types, same interface

var s Speaker = Dog{}
s = Cat{} // Both work
📦

Empty Interface

interface{} accepts any type

var anything interface{}
anything = 42

🔹 Basic Interface Example

Creating and using a simple interface:

type Shape interface {
    Area() float64
    Perimeter() float64
}

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)
}

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
}

// Function that works with any Shape
func PrintShapeInfo(s Shape) {
    fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 5, Height: 3}
    circle := Circle{Radius: 4}
    
    PrintShapeInfo(rect)   // Area: 15.00, Perimeter: 16.00
    PrintShapeInfo(circle) // Area: 50.27, Perimeter: 25.13
}

Output:

Area: 15.00, Perimeter: 16.00
Area: 50.27, Perimeter: 25.13

🔹 Empty Interface

The empty interface can hold values of any type:

func PrintAnything(value interface{}) {
    fmt.Printf("Value: %v, Type: %T\n", value, value)
}

func main() {
    PrintAnything(42)
    PrintAnything("hello")
    PrintAnything([]int{1, 2, 3})
    PrintAnything(true)
}

Output:

Value: 42, Type: int
Value: hello, Type: string
Value: [1 2 3], Type: []int
Value: true, Type: bool

🔹 Type Assertions

Extract the underlying value from an interface:

func ProcessValue(value interface{}) {
    // Type assertion with ok check
    if str, ok := value.(string); ok {
        fmt.Printf("String: %s (length: %d)\n", str, len(str))
        return
    }
    
    if num, ok := value.(int); ok {
        fmt.Printf("Integer: %d (doubled: %d)\n", num, num*2)
        return
    }
    
    fmt.Printf("Unknown type: %T\n", value)
}

func main() {
    ProcessValue("hello")
    ProcessValue(42)
    ProcessValue(3.14)
}

Output:

String: hello (length: 5)
Integer: 42 (doubled: 84)
Unknown type: float64

🔹 Interface Composition

Combine multiple interfaces into one:

type Reader interface {
    Read([]byte) (int, error)
}

type Writer interface {
    Write([]byte) (int, error)
}

// Composed interface
type ReadWriter interface {
    Reader
    Writer
}

type File struct {
    name string
}

func (f File) Read(data []byte) (int, error) {
    fmt.Printf("Reading from %s\n", f.name)
    return len(data), nil
}

func (f File) Write(data []byte) (int, error) {
    fmt.Printf("Writing to %s\n", f.name)
    return len(data), nil
}

func ProcessFile(rw ReadWriter) {
    data := make([]byte, 10)
    rw.Read(data)
    rw.Write(data)
}

func main() {
    file := File{name: "example.txt"}
    ProcessFile(file)
}

Output:

Reading from example.txt
Writing to example.txt

🧠 Test Your Knowledge

How does Go determine if a type implements an interface?