Go Cheatsheet

Interfaces

Use this Go reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

What Is an Interface

An interface defines a set of method signatures. Any type that implements all the methods implicitly satisfies the interface — no implements keyword.

type Animal interface {
    Sound() string
    Name() string
}

type Dog struct{ name string }
func (d Dog) Sound() string { return "woof" }
func (d Dog) Name() string  { return d.name }

type Cat struct{ name string }
func (c Cat) Sound() string { return "meow" }
func (c Cat) Name() string  { return c.name }

// Both Dog and Cat satisfy Animal
var a Animal = Dog{name: "Rex"}
fmt.Println(a.Sound())  // "woof"
a = Cat{name: "Whiskers"}
fmt.Println(a.Sound())  // "meow"

Interface Declaration

// Single method
type Stringer interface {
    String() string
}

// Multiple methods
type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

// Interface embedding — combines interfaces
type ReadWriteCloser interface {
    io.Reader
    io.Writer
    io.Closer
}

The Empty Interface

interface{} (or any since Go 1.18) accepts any value.

func printAny(v interface{}) {
    fmt.Printf("%T: %v\n", v, v)
}
printAny(42)
printAny("hello")
printAny([]int{1, 2})

// any is an alias
func printAny(v any) { ... }

// Slice of any
items := []any{1, "two", 3.0, true}

Type Assertions

Extract the concrete value from an interface value.

var a Animal = Dog{name: "Rex"}

// Single-value (panics if wrong type)
d := a.(Dog)
fmt.Println(d.name)

// Comma-ok (safe, no panic)
d, ok := a.(Dog)
if ok {
    fmt.Println("is a Dog:", d.name)
}

c, ok := a.(Cat)   // ok=false; c is zero Cat{}

Type Switch

Branch on the dynamic type of an interface value.

func describe(i interface{}) {
    switch v := i.(type) {
    case nil:
        fmt.Println("nil")
    case int:
        fmt.Printf("int: %d\n", v)
    case string:
        fmt.Printf("string: %q\n", v)
    case Dog:
        fmt.Printf("Dog named %s\n", v.name)
    case Animal:                  // matches any other Animal
        fmt.Printf("Animal: %s\n", v.Name())
    default:
        fmt.Printf("unknown type: %T\n", v)
    }
}

Standard Library Interfaces

These are the most commonly implemented interfaces:

InterfacePackageMethod(s)
errorbuiltinError() string
fmt.StringerfmtString() string
fmt.GoStringerfmtGoString() string
io.ReaderioRead([]byte) (int, error)
io.WriterioWrite([]byte) (int, error)
io.CloserioClose() error
io.ReadWriterioRead + Write
io.ReadWriteCloserioRead + Write + Close
io.SeekerioSeek(offset int64, whence int) (int64, error)
io.ByteReaderioReadByte() (byte, error)
io.RuneReaderioReadRune() (rune, int, error)
sort.InterfacesortLen() int, Less(i, j int) bool, Swap(i, j int)
http.Handlernet/httpServeHTTP(ResponseWriter, *Request)
http.ResponseWriternet/httpHeader(), Write(), WriteHeader()
encoding.TextMarshalerencodingMarshalText() ([]byte, error)
encoding.TextUnmarshalerencodingUnmarshalText([]byte) error
json.Marshalerencoding/jsonMarshalJSON() ([]byte, error)
json.Unmarshalerencoding/jsonUnmarshalJSON([]byte) error
context.ContextcontextDeadline, Done, Err, Value
sync.LockersyncLock(), Unlock()

Implementing fmt.Stringer

type Color struct{ R, G, B uint8 }

func (c Color) String() string {
    return fmt.Sprintf("#%02x%02x%02x", c.R, c.G, c.B)
}

c := Color{255, 128, 0}
fmt.Println(c)  // #ff8000

Implementing sort.Interface

type ByLength []string

func (b ByLength) Len() int           { return len(b) }
func (b ByLength) Less(i, j int) bool { return len(b[i]) < len(b[j]) }
func (b ByLength) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }

words := []string{"banana", "kiwi", "apple"}
sort.Sort(ByLength(words))

Implementing io.Reader

type ZeroReader struct{}

func (z ZeroReader) Read(p []byte) (int, error) {
    for i := range p { p[i] = 0 }
    return len(p), nil
}

Interface Values Internals

An interface value is a pair (type, value). It is nil only when BOTH are nil.

var r io.Reader          // (nil, nil) — nil interface
var f *os.File           // nil *os.File
r = f                    // (type=*os.File, value=nil) — NON-nil interface!

fmt.Println(r == nil)    // false — common gotcha

// To avoid: return concrete nil, not interface wrapping typed nil
func bad() error {
    var p *MyError = nil
    return p    // returns non-nil error!
}

func good() error {
    return nil  // returns nil interface
}

Interface Composition

type Saver interface {
    Save() error
}
type Loader interface {
    Load(id int) error
}
type Repository interface {
    Saver
    Loader
    Delete(id int) error
}

Pointer vs Value Receiver in Interface Implementation

type Greeter interface{ Greet() string }

type Person struct{ Name string }

// Value receiver — both Person and *Person satisfy Greeter
func (p Person) Greet() string { return "Hi, " + p.Name }

// Pointer receiver — only *Person satisfies the interface
func (p *Person) SetName(n string) { p.Name = n }

If a method is defined on *T, only *T (not T) satisfies an interface requiring that method.

Checking Interface Satisfaction at Compile Time

// Place this in your package — causes compile error if Dog doesn't satisfy Animal
var _ Animal = Dog{}       // value receiver methods only
var _ Animal = (*Dog)(nil) // pointer receiver methods

Generics and Interfaces (Go 1.18+)

Interface constraints restrict type parameters.

// Type constraint with method
type Stringer interface {
    String() string
}

func Print[T Stringer](v T) {
    fmt.Println(v.String())
}

// Type union constraint (only in type parameter context)
type Number interface {
    ~int | ~int32 | ~int64 | ~float32 | ~float64
}

func Sum[T Number](nums []T) T {
    var total T
    for _, n := range nums {
        total += n
    }
    return total
}

Sum([]int{1, 2, 3})         // 6
Sum([]float64{1.1, 2.2})   // 3.3

// cmp.Ordered (ordered types, Go 1.21+)
import "cmp"
func Min[T cmp.Ordered](a, b T) T {
    if a < b { return a }
    return b
}

Common Patterns

Dependency injection via interface

type EmailSender interface {
    Send(to, subject, body string) error
}

type UserService struct {
    mailer EmailSender
}

func NewUserService(m EmailSender) *UserService {
    return &UserService{mailer: m}
}

Middleware / decorator

type Handler interface {
    Handle(req string) string
}

type LoggingHandler struct {
    inner Handler
}

func (l LoggingHandler) Handle(req string) string {
    log.Printf("handling: %s", req)
    result := l.inner.Handle(req)
    log.Printf("result: %s", result)
    return result
}