#Go Interfaces Tutorial

Elliot Forbes Elliot Forbes · May 28, 2026 · 5 min read

Source Code: The full source code for this tutorial is on GitHub: TutorialEdge/Go-Interfaces-Tutorial

By the end of this tutorial, you’ll understand:

  • What interfaces are and why Go uses them
  • How to define your own interfaces
  • How Go satisfies interfaces implicitly — no implements keyword needed

Basic Example

If you’re new to Go, you’ll see interface{} (or its modern alias any) frequently in code. A function that accepts interface{} will accept a value of any type.

package main

import (
    "fmt"
)

func myFunc(a any) {
    fmt.Println(a)
}

func main() {
    var my_age int
    my_age = 25

    myFunc(my_age)
}

Note: any was introduced in Go 1.18 as an alias for interface{}. They are identical — prefer any in modern Go code.

$ go run main.go
25

Why is this Useful?

By accepting any, a function can work with integers, strings, structs, or anything else without knowing the type upfront. It’s Go’s way of saying: “I don’t care what this is, just give me something.”

This flexibility comes with a trade-off — you lose type safety and must use a type assertion to get the concrete value back. Use any sparingly; prefer typed interfaces when you know what behaviour you need.

Defining Interfaces

An interface in Go defines a set of method signatures — a contract. Any type that implements all those methods automatically satisfies the interface. There is no implements keyword.

Say we want a Guitarist interface with a PlayGuitar() method:

type Guitarist interface {
    PlayGuitar()
}

Any type with a PlayGuitar() method satisfies Guitarist — even if it was written without knowing this interface exists. This implicit satisfaction is one of Go’s most powerful features.

package main

import "fmt"

type Guitarist interface {
    PlayGuitar()
}

type BaseGuitarist struct {
    Name string
}

type AcousticGuitarist struct {
    Name string
}

func (b BaseGuitarist) PlayGuitar() {
    fmt.Printf("%s plays the Bass Guitar\n", b.Name)
}

func (b AcousticGuitarist) PlayGuitar() {
    fmt.Printf("%s plays the Acoustic Guitar\n", b.Name)
}

func main() {
    var player BaseGuitarist
    player.Name = "Paul"
    player.PlayGuitar()

    var player2 AcousticGuitarist
    player2.Name = "Ringo"
    player2.PlayGuitar()
}

Because both types implement PlayGuitar(), we can store them together in a []Guitarist slice:

var guitarists []Guitarist
guitarists = append(guitarists, player)
guitarists = append(guitarists, player2)

This lets you write functions that operate on any Guitarist without knowing the concrete type.

Return Values

Real-world interfaces typically include methods with return values. Go supports multiple return values, including error returns, in interface definitions:

type Employee interface {
    Name() string
    Language() string
    Age() int
    Random() (string, error)
}

Any type that implements all four methods satisfies Employee. If it implements only some of them, the compiler will reject it.

Satisfying Interfaces

The Go compiler enforces interface satisfaction at compile time. If a type is missing any method, the program won’t compile.

package main

type Employee interface {
    Language() string
    Age() int
    Random() (string, error)
}

type Engineer struct {
    Name string
}

func (e Engineer) Language() string {
    return e.Name + " programs in Go"
}

func main() {
    // This will throw a compile error
    var programmers []Employee
    elliot := Engineer{Name: "Elliot"}
    // Engineer does not implement Employee:
    // missing Age() and Random()
    programmers = append(programmers, elliot)
}

The compiler error tells you exactly which methods are missing — making interfaces a reliable way to enforce contracts across a codebase.

Frequently Asked Questions

What is an interface in Go?

An interface is a named set of method signatures. Any type that implements all the methods in an interface automatically satisfies it — there is no implements keyword. This implicit satisfaction makes Go interfaces extremely flexible and composable.

What is the difference between interface{} and any in Go?

They are identical. any was introduced in Go 1.18 as a more readable alias for interface{}. Both represent the empty interface — a type with no method requirements that accepts any value. Prefer any in modern Go code.

How does Go know if a type satisfies an interface?

The compiler checks at compile time whether the type has all the methods the interface requires — with matching names, parameter types, and return types. If any method is missing, the build fails with a clear error message.

Can a Go type satisfy multiple interfaces?

Yes. A type can satisfy any number of interfaces simultaneously, as long as it implements all the required methods for each. This is how Go achieves polymorphism without inheritance.

When should I use interfaces in Go?

Use interfaces when you want to write code that works with multiple types, when you want to make code testable by swapping implementations, or when you want to define a contract between packages. Avoid defining interfaces prematurely — define them where they are consumed, not where types are defined.

What is an empty interface in Go?

An empty interface (any or interface{}) has no method requirements, so every type satisfies it. It is useful for generic containers or functions that must accept any value, but it sacrifices type safety. Use typed interfaces whenever possible.

Conclusion

Interfaces are one of Go’s most important features. They let you write flexible, testable code by defining contracts rather than concrete types — and Go’s implicit satisfaction means any type automatically qualifies if it has the right methods.

Key takeaways:

  • Interfaces define method sets — not implementations
  • Satisfaction is implicit — no implements keyword
  • Use any instead of interface{} in Go 1.18+
  • The compiler enforces interface satisfaction at build time

For more on Go’s type system, check out the Go Structs Tutorial and Go Methods Tutorial.