Question

Understanding "does not implement interface" with Pointer Receivers in Go

gopointersmethodsinterface

Question

In Go, I often see compiler errors like this:

X does not implement Y (... method has a pointer receiver)

There are already several questions and answers about this error, but many of them focus on specific cases. I want a broader explanation.

What are the common situations that cause this error? How do method sets, interfaces, and pointer receivers interact in Go? What are the usual ways to avoid the problem, and how can I diagnose it when it happens?

Short Answer

By the end of this page, you will understand why a Go type may fail to implement an interface when one or more methods use pointer receivers. You will learn how Go method sets work for values and pointers, how interface assignment checks those method sets, what common mistakes cause this error, and how to choose the right fix in real code.

Concept

In Go, a type implements an interface implicitly. That means you do not write implements; instead, the compiler checks whether the type has all the methods required by the interface.

The key detail is that Go treats the method set of a value type differently from the method set of a pointer type.

Consider this type:

type User struct {
    Name string
}

func (u User) Display() {
    fmt.Println(u.Name)
}

func (u *User) Rename(name string) {
    u.Name = name
}

There are two method sets here:

  • User has the methods with receiver User
  • *User has the methods with receiver User and *User

So:

  • A User value has Display()
  • A *User pointer has both Display() and Rename()

If an interface requires Rename(), then User does not implement it, but *User does.

That is why this error appears.

Why this matters

This affects:

  • assigning a value to an interface
  • passing values into functions that accept interfaces
  • returning interface values
  • storing values in interface-typed fields, maps, or slices

Because interfaces are checked against method sets, whether you pass x or &x can change whether the code compiles.

The rule to remember

If a method has a pointer receiver, only the pointer type is guaranteed to implement an interface requiring that method.

In short:

  • T gets methods declared on T
  • *T gets methods declared on T and *T
  • If the interface needs a pointer-receiver method, use *T

This is the core reason behind the error.

Mental Model

Think of a value type and a pointer type as two workers with different tool belts.

  • The value worker (T) carries only the tools meant for plain values.
  • The pointer worker (*T) carries all the value tools plus the pointer-only tools.

An interface is like a job description.

If the job requires a tool that exists only on the pointer worker's belt, you cannot assign the value worker to that job.

So if the interface says:

  • Read()
  • Update()

and Update() is only defined on *T, then only *T is qualified for the job.

A plain T is missing part of the required tool set.

Take Quiz

Syntax and Examples

Core syntax

Value receiver

func (t T) MethodName() {
    // works on a copy of T
}

Pointer receiver

func (t *T) MethodName() {
    // works on a pointer to T
}

Interface

type Doer interface {
    Do()
}

Example 1: value receiver works for both T and *T

package main

import "fmt"

type Speaker interface {
    Speak()
}

type Person struct {
    Name string
}

func (p Person) Speak() {
    fmt.Println("Hello,", p.Name)
}

func main() {
    var s1 Speaker = Person{Name: "Ava"}
    var s2 Speaker = &Person{Name: "Liam"}

    s1.Speak()
    s2.Speak()
}

Because Speak() has a value receiver, both Person and *Person satisfy Speaker.

Example 2: pointer receiver means only *T implements the interface

package main

import "fmt"

type Renamer interface {
    Rename(string)
}

type Person struct {
    Name string
}

func (p *Person) Rename(name string) {
    p.Name = name
}

func main() {
    p := Person{Name: "Ava"}

    // var r Renamer = p   // compile error
    var r Renamer = &p     // correct

    r.Rename("Mia")
    fmt.Println(p.Name)
}

The commented line fails because Person does not have Rename() in its method set. Only *Person does.

Example 3: mixed receivers

package main

import "fmt"

type User struct {
    Name string
}

func (u User) Print() {
    fmt.Println(u.Name)
}

func (u *User) SetName(name string) {
    u.Name = name
}

type Printer interface {
    Print()
}

type NameSetter interface {
    SetName(string)
}

func main() {
    u := User{Name: "Noah"}

    var p Printer = u    // OK
    p.Print()

    // var s NameSetter = u // compile error
    var s NameSetter = &u  // OK
    s.SetName("Emma")

    fmt.Println(u.Name)
}

This shows exactly why some interfaces work with a value while others require a pointer.

Step by Step Execution

Consider this example:

package main

import "fmt"

type Writer interface {
    Write(string)
}

type Document struct {
    Text string
}

func (d *Document) Write(s string) {
    d.Text += s
}

func main() {
    doc := Document{Text: "Hello"}

    // var w Writer = doc // compile error
    var w Writer = &doc

    w.Write(" world")
    fmt.Println(doc.Text)
}

Step by step

1. Define the interface

type Writer interface {
    Write(string)
}

Any type assigned to Writer must have a Write(string) method in its method set.

2. Define the struct

type Document struct {
    Text string
}

So far, Document has no methods.

3. Add a pointer receiver method

func (d *Document) Write(s string) {
    d.Text += s
}

This method belongs to *Document, not Document.

Method sets now look like this:

  • Document: no Write
  • *Document: has Write

4. Create a value

doc := Document{Text: "Hello"}

doc is a value of type Document.

5. Try interface assignment

var w Writer = &doc

This works because &doc is a *Document, and *Document has Write(string).

If you wrote:

var w Writer = doc

it would fail, because Document does not implement Writer.

6. Call the interface method

w.Write(" world")

The interface holds a *Document, so it calls the pointer receiver method.

7. Check the result

fmt.Println(doc.Text)

Output:

Hello world

The original doc changed because the method worked through a pointer.

Real World Use Cases

Pointer receiver interface issues appear often in normal Go programs.

1. Mutating structs

If a method changes the receiver, it usually uses a pointer receiver.

func (c *Counter) Increment() {
    c.Value++
}

If an interface requires Increment(), you must usually pass *Counter.

2. Avoiding large copies

For large structs, pointer receivers avoid copying data.

func (r *Report) Process() error {
    // work on a large struct
    return nil
}

If Process() is part of an interface, only *Report implements it.

3. Standard library patterns

Many standard library types are used through pointers because their methods mutate internal state.

Examples include writers, buffers, decoders, and encoders.

var b bytes.Buffer
// io.Writer is implemented by *bytes.Buffer when used through its pointer

4. Dependency injection

A service may depend on an interface, and a concrete implementation may use pointer receiver methods.

type Store interface {
    Save(string) error
}

If FileStore.Save has a pointer receiver, inject *FileStore, not FileStore.

5. HTTP handlers and app state

Application structs often store shared state and use pointer receiver methods.

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // use shared dependencies
}

In such cases, *App implements http.Handler. A plain App value may not.

Take Quiz

Real Codebase Usage

In real projects, developers use a few common patterns to avoid pointer-receiver interface problems.

Guard your interface assignments

A common compile-time check is:

var _ io.Writer = (*MyWriter)(nil)

This confirms at compile time that *MyWriter implements io.Writer.

If you accidentally expect MyWriter to implement it, the mismatch becomes obvious.

Use pointer receivers consistently when methods mutate state

If one method needs a pointer receiver, many teams make all methods on that type pointer receivers for consistency.

func (s *Session) Start() {}
func (s *Session) Stop() {}
func (s *Session) Reset() {}

This reduces confusion about whether to use T or *T.

Accept interfaces, return concrete types

A common Go pattern is:

  • functions accept interfaces
  • constructors return concrete types, often pointers
func NewService() *Service {
    return &Service{}
}

Then callers naturally pass the pointer where needed.

Dependency fields often store interface values

type App struct {
    Store Store
}

When wiring the app:

app := App{Store: &FileStore{}}

If you accidentally write FileStore{} instead of &FileStore{}, you may get the pointer receiver error.

Validation and error handling

These issues often appear during initialization code, where implementations are assigned to interfaces. Good constructors and compile-time checks make the errors easier to spot.

Common Mistakes

1. Passing a value when the interface requires pointer methods

Broken code:

type Runner interface {
    Run()
}

type Job struct{}

func (j *Job) Run() {}

func start(r Runner) {}

func main() {
    j := Job{}
    start(j) // compile error
}

Fix:

start(&j)

2. Assuming automatic method call rules also apply to interfaces

Go sometimes lets you call a pointer receiver method on an addressable value:

j := Job{}
j.Run() // allowed if j is addressable

Beginners often think that means j also implements the interface. It does not.

Method call convenience is not the same as interface method set rules.

3. Mixing value and pointer receivers without understanding the effect

func (u User) Print() {}
func (u *User) Save() {}

Now:

  • User implements interfaces needing Print
  • *User implements interfaces needing Print and/or Save

This is legal, but can confuse readers if done without a reason.

4. Returning a value when callers expect an interface implemented by a pointer

Broken code:

type Closer interface {
    Close() error
}

type File struct{}

func (f *File) Close() error { return nil }

func NewFile() File {
    return File{}
}

If someone tries to use NewFile() as a Closer, it fails.

Better:

func NewFile() *File {
    return &File{}
}

5. Storing the wrong form in interface fields

Broken code:

type Saver interface {
    Save() error
}

type DB struct{}

func (d *DB) Save() error { return nil }

type App struct {
    Saver Saver
}

func main() {
    app := App{
        Saver: DB{}, // compile error
    }
    _ = app
}

Fix:

app := App{
    Saver: &DB{},
}

Comparisons

Value receivers vs pointer receivers

TopicValue receiver func (t T)Pointer receiver func (t *T)
Method belongs toT*T
Available on T method setYesNo
Available on *T method setYesYes
Can modify original valueNo, works on a copyYes
Copies receiver dataUsually yesNo extra struct copy
Interface implemented by TYes, for this methodNo
Interface implemented by *TYesYes

Method call convenience vs interface implementation

SituationAllowed?Why
value.PointerMethod() on an addressable valueOften yesGo can implicitly take the address for the call
Assigning value to an interface requiring that pointer methodNoInterface checks the method set of T, not call convenience
Assigning &value to that interfaceYes*T contains pointer receiver methods

When to choose each

Use caseBetter choice
Method should not modify receiver and type is smallValue receiver
Method must modify receiverPointer receiver
Type contains mutexes or large fieldsPointer receiver
Interface should be implementable by both T and *TValue receiver, if appropriate
Take Quiz

Cheat Sheet

Quick rules

  • A type implements an interface if its method set contains all required methods.
  • T has methods with receiver T.
  • *T has methods with receiver T and *T.
  • If an interface requires a method defined on *T, then only *T implements that interface.

Fast diagnosis

If you see:

X does not implement Y (... method has a pointer receiver)

check these:

  1. Is the missing method declared as func (x *X) Method()?
  2. Are you passing X where *X is needed?
  3. Are you assigning a value instead of a pointer to an interface?
  4. Is your constructor returning X when it should return *X?
  5. Are mixed receiver types causing confusion?

Common fix

Change this:

var y Y = x

into this:

var y Y = &x

or create the value as a pointer from the start:

x := &X{}

Compile-time interface check

var _ Y = (*X)(nil)

Important edge case

This may compile:

x.Method()

but this may still fail:

var y Y = x

because direct method calls and interface implementation follow related but different rules.

FAQ

Why does x.SomeMethod() work, but x still does not implement the interface?

Because Go may automatically take the address for a direct method call on an addressable value. Interface implementation does not use that convenience; it checks the actual method set of the type.

Does *T get methods defined on T?

Yes. The method set of *T includes methods with receiver T and methods with receiver *T.

Does T get methods defined on *T?

No. A value type does not get pointer receiver methods in its method set.

Should I always use pointer receivers in Go?

No. Use pointer receivers when you need to modify the receiver, avoid copying large structs, or keep method behavior consistent. Small immutable-like types can use value receivers.

How do I know whether to pass x or &x to an interface?

Check how the required methods are declared. If any required method uses a pointer receiver on your concrete type, pass &x.

Can a type with mixed receivers still be valid?

Yes. It is valid Go, but it can be confusing. Be consistent unless you have a clear reason.

Is this about interfaces only?

No. Pointer receivers affect method sets in general, but the confusion most often appears when assigning concrete types to interfaces.

Take Quiz

Mini Project

Description

Build a small logging system with an interface and a concrete logger type. This project demonstrates how pointer receiver methods affect whether a struct can be assigned to an interface. It also shows a realistic reason for pointer receivers: updating internal state such as counters or stored messages.

Goal

Create a logger that satisfies an interface only when used as a pointer, then use it to record messages and count how many were written.

Requirements

  • Define a Logger interface with a Log(string) method and a Count() int method.
  • Create a MemoryLogger struct that stores log messages.
  • Implement Log(string) so it adds a message to the struct.
  • Implement Count() int so it returns the number of stored messages.
  • Use the logger through the interface and print the final count.
Take Quiz

Keep learning

Related questions

View more