Skip to main content

Interfaces

Go interfaces define a set of methods that a type must implement. Unlike other languages, Go interfaces are implemented implicitly. Without any implements keyword, if a type has all the methods an interface requires, it automatically satisfies that interface.

Interface Basics​

An interface is a collection of method signatures.

package main

import (
"fmt"
"math"
)

// Interface definition
type Shape interface {
Area() float64
Perimeter() float64
}

type Circle struct {
Radius float64
}

// Circle implements Shape β€” automatically, no declaration needed
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}

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

// Accepts a Shape interface parameter
func printShapeInfo(s Shape) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 4, Height: 6},
Circle{Radius: 3},
}

for _, s := range shapes {
printShapeInfo(s)
}
// Area: 78.54, Perimeter: 31.42
// Area: 24.00, Perimeter: 20.00
// Area: 28.27, Perimeter: 18.85
}

Benefits of Implicit Implementation​

Go's implicit interface implementation makes code more flexible. Even interfaces defined later are automatically implemented by existing types.

package main

import (
"fmt"
"strings"
)

// Existing types automatically implement later-defined interfaces
type Describer interface {
Describe() string
}

type Dog struct {
Name string
Breed string
}

func (d Dog) Describe() string {
return fmt.Sprintf("%s is a %s", d.Name, d.Breed)
}

type Car struct {
Make string
Model string
Year int
}

func (c Car) Describe() string {
return fmt.Sprintf("%d %s %s", c.Year, c.Make, c.Model)
}

func printAll(items []Describer) {
for _, item := range items {
fmt.Println(item.Describe())
}
}

func main() {
items := []Describer{
Dog{Name: "Rex", Breed: "Jindo"},
Car{Make: "Hyundai", Model: "Elantra", Year: 2024},
Dog{Name: "Luna", Breed: "Poodle"},
}
printAll(items)

// strings.Builder implements multiple interfaces without explicit declarations
var sb strings.Builder
fmt.Fprintln(&sb, "Hello, Go!")
fmt.Fprintln(&sb, "Interfaces are implicit")
fmt.Print(sb.String())
}

Interface Embedding​

Interfaces can embed other interfaces.

package main

import (
"fmt"
"io"
"strings"
)

// Basic interfaces
type Reader interface {
Read() string
}

type Writer interface {
Write(s string)
}

// Interface embedding β€” includes both Reader and Writer
type ReadWriter interface {
Reader
Writer
}

// With additional method
type ReadWriteCloser interface {
ReadWriter
Close() error
}

type Buffer struct {
data strings.Builder
}

func (b *Buffer) Read() string {
return b.data.String()
}

func (b *Buffer) Write(s string) {
b.data.WriteString(s)
}

func (b *Buffer) Close() error {
b.data.Reset()
return nil
}

func useReadWriter(rw ReadWriter) {
rw.Write("Hello, ")
rw.Write("World!")
fmt.Println(rw.Read())
}

func main() {
buf := &Buffer{}
useReadWriter(buf) // "Hello, World!"

buf.Close()

// Standard library also uses interface embedding
// io.ReadWriter = io.Reader + io.Writer
var rw io.ReadWriter = strings.NewReader("test")
_ = rw
fmt.Println("Standard library also uses interface embedding")
}

The Empty Interface β€” any​

interface{} (aliased as any since Go 1.18) has no methods, so every type implements it.

package main

import "fmt"

// any = interface{} (Go 1.18+)
func printAnything(v any) {
fmt.Printf("value: %v, type: %T\n", v, v)
}

func storeAll(items ...any) []any {
return items
}

// Generic container (using any)
type Container struct {
items []any
}

func (c *Container) Add(item any) {
c.items = append(c.items, item)
}

func (c *Container) Get(index int) any {
if index < 0 || index >= len(c.items) {
return nil
}
return c.items[index]
}

func (c *Container) Len() int {
return len(c.items)
}

func main() {
printAnything(42) // value: 42, type: int
printAnything("hello") // value: hello, type: string
printAnything(3.14) // value: 3.14, type: float64
printAnything([]int{1, 2}) // value: [1 2], type: []int
printAnything(nil) // value: <nil>, type: <nil>

items := storeAll(1, "two", 3.0, true, []byte("bytes"))
for _, item := range items {
fmt.Printf("%T: %v\n", item, item)
}

c := &Container{}
c.Add(42)
c.Add("hello")
c.Add([]float64{1.1, 2.2})

// To extract a concrete type from any, use type assertion (see ch7.2)
if n, ok := c.Get(0).(int); ok {
fmt.Println("integer:", n*2)
}
}

Internal Structure of Interface Values​

An interface value is internally composed of a (type, value) pair.

package main

import (
"fmt"
"reflect"
)

type Doer interface {
Do() string
}

type TypeA struct{ name string }
type TypeB struct{ value int }

func (a TypeA) Do() string { return "A: " + a.name }
func (b TypeB) Do() string { return fmt.Sprintf("B: %d", b.value) }

func inspectInterface(d Doer) {
fmt.Printf("value: %v\n", d)
fmt.Printf("type: %T\n", d)
v := reflect.ValueOf(d)
fmt.Printf("reflect type: %v\n", v.Type())
fmt.Printf("reflect value: %v\n", v)
fmt.Println()
}

func main() {
var d Doer

fmt.Println("nil interface:", d == nil) // true

d = TypeA{name: "foo"}
inspectInterface(d)

d = TypeB{value: 42}
inspectInterface(d)

// nil interface pitfall
var a *TypeA = nil
d = a // (type=*TypeA, value=nil) β€” interface is NOT nil!
fmt.Println("after nil pointer assign:", d == nil) // false!

// Correct nil check
if d != nil {
if a, ok := d.(*TypeA); ok && a != nil {
fmt.Println(a.Do())
}
}
}

Practical Example: Plugin System​

package main

import (
"fmt"
"sort"
"strings"
)

// Plugin interface
type Plugin interface {
Name() string
Execute(input string) string
}

// Plugin registry
type Registry struct {
plugins map[string]Plugin
}

func NewRegistry() *Registry {
return &Registry{plugins: make(map[string]Plugin)}
}

func (r *Registry) Register(p Plugin) {
r.plugins[p.Name()] = p
}

func (r *Registry) Execute(name, input string) (string, bool) {
p, ok := r.plugins[name]
if !ok {
return "", false
}
return p.Execute(input), true
}

func (r *Registry) ListPlugins() []string {
names := make([]string, 0, len(r.plugins))
for name := range r.plugins {
names = append(names, name)
}
sort.Strings(names)
return names
}

// Plugin implementations
type UpperPlugin struct{}

func (p UpperPlugin) Name() string { return "upper" }
func (p UpperPlugin) Execute(s string) string { return strings.ToUpper(s) }

type ReversePlugin struct{}

func (p ReversePlugin) Name() string { return "reverse" }
func (p ReversePlugin) Execute(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}

type RepeatPlugin struct {
Times int
}

func (p RepeatPlugin) Name() string { return "repeat" }
func (p RepeatPlugin) Execute(s string) string { return strings.Repeat(s, p.Times) }

func main() {
reg := NewRegistry()
reg.Register(UpperPlugin{})
reg.Register(ReversePlugin{})
reg.Register(RepeatPlugin{Times: 3})

fmt.Println("Registered plugins:", reg.ListPlugins())

inputs := []struct {
plugin string
input string
}{
{"upper", "hello world"},
{"reverse", "golang"},
{"repeat", "Go! "},
{"unknown", "test"},
}

for _, tc := range inputs {
result, ok := reg.Execute(tc.plugin, tc.input)
if ok {
fmt.Printf("[%s] %q β†’ %q\n", tc.plugin, tc.input, result)
} else {
fmt.Printf("[%s] plugin not found\n", tc.plugin)
}
}
}

Interface Design Principles​

package main

import "fmt"

// Bad: interface too large
type BigInterface interface {
Read() string
Write(s string)
Close() error
Flush() error
Seek(offset int) error
Len() int
// ... too many methods
}

// Good: small, focused interfaces
type Reader interface {
Read() string
}

type Writer interface {
Write(s string)
}

type Closer interface {
Close() error
}

// Compose as needed
type ReadCloser interface {
Reader
Closer
}

// Single-method interfaces β€” most powerful
type Stringer interface {
String() string
}

type Logger interface {
Log(msg string)
}

// Function type implementing an interface
type LoggerFunc func(msg string)

func (f LoggerFunc) Log(msg string) {
f(msg)
}

func doWork(l Logger) {
l.Log("work started")
l.Log("work finished")
}

func main() {
// Implement with a function
doWork(LoggerFunc(func(msg string) {
fmt.Println("[LOG]", msg)
}))

// Implement with a struct
doWork(LoggerFunc(func(msg string) {
fmt.Printf("[FILE] %s\n", msg)
}))
}

Key Summary

  • Go interfaces use implicit implementationβ€” no implements declaration needed
  • Interface internals: (dynamic type, dynamic value) pair
  • any (interface{}) can hold any type β€” the empty interface
  • Design interfaces smallβ€” single-method interfaces are the most flexible
  • nil interface β‰  interface holding a nil pointer (watch out!)
  • Embed interfaces to compose larger interfaces