Go Tutorials: Go Interfaces & Implicit Interface Implementation

These two ideas are closely related. The easiest mental model is:

Interface = a list of behaviors.
Implicit implementation = Go automatically accepts any type that has those behaviors.

You never write implements in Go.


1. Interfaces

What is an interface?

An interface describes what something can do, without saying how it does it.

For example:

type Speaker interface {
    Speak() string
}

This means:

Anything that has a method Speak() string can be treated as a Speaker.

The interface does not contain the implementation.


Why use interfaces?

Interfaces help you write code that works with different types without caring about their exact type.

Imagine:

  • Dog can speak
  • Person can speak
  • Robot can speak

Your program doesn’t necessarily need to know whether something is a Dog, Person, or Robot.

It only needs to know:

“Can this thing Speak()?”


Complete Example — Interface

Copy and run:

package main

import "fmt"

// Speaker describes a behavior.
//
// Any type that has:
//     Speak() string
//
// can be used as a Speaker.
type Speaker interface {
	Speak() string
}

// Dog is a normal struct.
type Dog struct {
	Name string
}

// Dog has the Speak method.
func (d Dog) Speak() string {
	return d.Name + " says Woof!"
}

// Person is another normal struct.
type Person struct {
	Name string
}

// Person also has the Speak method.
func (p Person) Speak() string {
	return p.Name + " says Hello!"
}

// This function does NOT care
// whether it receives Dog, Person, etc.
//
// It only requires something
// that satisfies the Speaker interface.
func makeItSpeak(s Speaker) {
	fmt.Println(s.Speak())
}

func main() {
	dog := Dog{Name: "Buddy"}
	person := Person{Name: "John"}

	makeItSpeak(dog)
	makeItSpeak(person)
}

Output:

Buddy says Woof!
John says Hello!

Understand the important part

Look at this:

type Speaker interface {
	Speak() string
}

The interface says:

I need something with Speak() string

Then:

func (d Dog) Speak() string

Dog has that method.

And:

func (p Person) Speak() string

Person also has that method.

Therefore both can be passed here:

func makeItSpeak(s Speaker)

What happened?

Conceptually:

flowchart TD
    A["Speaker interface"] --> B["Requires: Speak() string"]
    C["Dog"] --> D["Has Speak() string"]
    E["Person"] --> F["Has Speak() string"]
    D --> G["Can be used as Speaker"]
    F --> G

Why is this useful?

Without the interface, you might write:

func makeDogSpeak(d Dog) {
	fmt.Println(d.Speak())
}

func makePersonSpeak(p Person) {
	fmt.Println(p.Speak())
}

With an interface:

func makeItSpeak(s Speaker) {
	fmt.Println(s.Speak())
}

One function works with many different types.


When should I use an interface?

Use an interface when several types should be usable through the same behavior.

For example:

Writer
    Write()

Reader
    Read()

Notifier
    Notify()

PaymentProcessor
    Pay()

Storage
    Save()

Speaker
    Speak()

Think:

“I don’t care exactly WHAT you are. I care about WHAT YOU CAN DO.”


Where are interfaces commonly used?

You’ll see them in:

  • function parameters
  • testing and mocks
  • database abstractions
  • HTTP code
  • file handling
  • logging
  • external services
  • application architecture

For a beginner, the most important use is:

func doSomething(x SomeInterface)

It allows different types to be passed to the same function.


2. Implicit Interface Implementation

This is one of the most important differences between Go and languages such as Java.

In Go, you do not explicitly declare that a type implements an interface.

Go checks the methods automatically.


The key rule

Suppose we have:

type Notifier interface {
	Notify()
}

And:

type Email struct{}

func (e Email) Notify() {
	fmt.Println("Sending email")
}

Go automatically knows:

Email has Notify()

Notifier requires Notify()

Therefore:

Email satisfies Notifier

There is no:

implements Notifier

That is why it is called implicit interface implementation.


Complete Example — Implicit Interface Implementation

Copy and run:

package main

import "fmt"

// Notifier says:
// "Any type with Notify() can be a Notifier."
type Notifier interface {
	Notify()
}

// Email is just a normal struct.
type Email struct {
	Address string
}

// Email has the method required by Notifier.
//
// Notice:
// We NEVER wrote:
//     Email implements Notifier
//
// Go figures it out automatically.
func (e Email) Notify() {
	fmt.Println("Sending email to:", e.Address)
}

// SMS is another normal struct.
type SMS struct {
	Phone string
}

// SMS also has Notify().
//
// Therefore SMS ALSO automatically
// satisfies the Notifier interface.
func (s SMS) Notify() {
	fmt.Println("Sending SMS to:", s.Phone)
}

// sendNotification accepts ANY type
// that satisfies Notifier.
func sendNotification(n Notifier) {
	n.Notify()
}

func main() {
	email := Email{
		Address: "john@example.com",
	}

	sms := SMS{
		Phone: "1234567890",
	}

	sendNotification(email)
	sendNotification(sms)
}

Output:

Sending email to: john@example.com
Sending SMS to: 1234567890

What exactly makes Email a Notifier?

The interface requires:

type Notifier interface {
	Notify()
}

Email has:

func (e Email) Notify()

So Go says:

Notifier requirement
        ↓
    Notify()
        ↑
Email has Notify()
        ↓
Email satisfies Notifier

Nothing else is required.


There is no implements

This is the part worth remembering.

In some languages you might see something conceptually like:

class Email implements Notifier

Go does not work like that.

You simply write:

type Email struct{}

and:

func (e Email) Notify() {
	fmt.Println("Email sent")
}

That’s enough.

Go notices that the method matches the interface.


How does Go decide?

Go compares the interface’s required methods with the type’s methods.

Example:

type Notifier interface {
	Notify()
}

Type A:

type Email struct{}

func (Email) Notify() {}

✅ Implements Notifier.

Type B:

type SMS struct{}

func (SMS) Notify() {}

✅ Implements Notifier.

Type C:

type Car struct{}

func (Car) Drive() {}

❌ Does NOT implement Notifier.

Why?

Because Car has:

Drive()

but the interface requires:

Notify()

What if an interface has multiple methods?

Then the type must have all of them.

For example:

type Device interface {
	Start()
	Stop()
}

This satisfies it:

type Computer struct{}

func (Computer) Start() {}
func (Computer) Stop() {}

But this does not:

type Computer struct{}

func (Computer) Start() {}

because:

Start() ✅
Stop()  ❌

All required methods must exist.


Interfaces vs Implicit Implementation

ConceptInterfaceImplicit Implementation
What is it?Defines required behaviorsDetermines whether a type satisfies an interface
Exampletype Speaker interface { Speak() }Dog has Speak(), so Dog satisfies Speaker
Contains data?NoNot applicable
Contains required methods?YesType must provide those methods
Need implements keyword?NoNo
Checked by Go?YesYes
Main purposeDefine behaviorConnect types to interfaces automatically
Think of it as“What must you do?”“You can already do it, so you qualify.”

Interface vs Struct

This distinction is also important.

StructInterface
Describes dataDescribes behavior
Contains fieldsContains method requirements
Name stringSpeak() string
Represents what something isRepresents what something can do
Usually creates concrete valuesUsually used to accept multiple types

Example struct:

type Dog struct {
	Name string
	Age  int
}

This describes data.

Example interface:

type Speaker interface {
	Speak() string
}

This describes behavior.


The relationship between everything

flowchart TD
    A["Interface"] --> B["Defines required methods"]

    B --> C["Speak() string"]

    D["Dog"] --> E["Has Speak() string"]
    F["Person"] --> G["Has Speak() string"]
    H["Car"] --> I["Has Drive()"]

    E --> J["Satisfies Speaker"]
    G --> J
    I --> K["Does NOT satisfy Speaker"]

    J --> L["Can be passed to function expecting Speaker"]

The Mental Model

Don’t think:

Dog IS a Speaker

Instead think:

Dog CAN Speak

Therefore:

Dog can be USED AS a Speaker.

That is very close to how Go interfaces are intended to be understood.


What → Why → When → How → Where

QuestionAnswer
What?An interface defines methods that a type must have
Why?So one piece of code can work with many different types
When?When different types share the same behavior
How?Define an interface and give types matching methods
Where?Commonly in function parameters, libraries, services, testing and abstractions

For implicit implementation:

QuestionAnswer
What?Automatic interface satisfaction
Why?Removes explicit implements declarations
When?Whenever a type’s methods match an interface
How?Simply implement all required methods
Where?Everywhere interfaces are used in Go

The 5 Rules to Remember

  1. An interface describes behavior.
type Speaker interface {
	Speak()
}
  1. A type satisfies an interface by having all required methods.
func (Dog) Speak() {}
  1. There is no implements keyword.

You never write:

Dog implements Speaker
  1. Multiple unrelated types can satisfy the same interface.
Dog    → Speak()
Person → Speak()
Robot  → Speak()

All three can satisfy Speaker.

  1. Functions can accept the interface instead of the concrete type.
func talk(s Speaker) {
	s.Speak()
}

Now talk() can work with every compatible type.


One-Sentence Summary

The entire concept can be reduced to:

An interface says “I need these methods”; any Go type that has those methods automatically satisfies the interface.

So whenever you see:

type X interface {
	DoSomething()
}

read it mentally as:

“Give me anything that knows how to DoSomething().”

That mental model will make Go interfaces much easier to understand.

Related Posts

Go Tutorials: Go Methods

Go methods become much easier once you understand one idea: A method is simply a function attached to a type. And the important rule is: A receiver…

Read More

Go Tutorials: Modules, Packages, Subpackages, Submodules, and Workspaces

This tutorial gives you one complete mental model for: The most important idea is: And sometimes: These two designs are very different. 1. What is a Go…

Read More

Go Tutorials: Numeric Types Beginner Tutorial

Go has four main families of numeric types: The simplest way to remember them is: Type family Stores Example int Whole numbers, positive or negative -10, 0,…

Read More

Go Tutorials: Go Testing, Benchmarking, and Profiling

These three topics become much easier once you separate the questions they answer: Topic Main question Main Go tool Result Testing Does my code work correctly? go…

Read More

Go Tutorials: Generics

1. What are Generics? Generics let you write one piece of code that works with multiple types while keeping Go’s compile-time type safety. Suppose you want a…

Read More

Go Concurrency Management Made Simple

This tutorial covers: The goal is: One concept → one purpose → one complete runnable example. Every example is independent. Save any example as: and run: 0….

Read More