Question
Understanding "does not implement interface" with Pointer Receivers in Go
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:
Userhas the methods with receiverUser*Userhas the methods with receiverUserand*User
So:
- A
Uservalue hasDisplay() - A
*Userpointer has bothDisplay()andRename()
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.
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() {
s1 Speaker = Person{Name: }
s2 Speaker = &Person{Name: }
s1.Speak()
s2.Speak()
}
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
}
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
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
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() {}
Save() {}
Comparisons
Value receivers vs pointer receivers
| Topic | Value receiver func (t T) | Pointer receiver func (t *T) |
|---|---|---|
| Method belongs to | T | *T |
Available on T method set | Yes | No |
Available on *T method set | Yes | Yes |
| Can modify original value | No, works on a copy | Yes |
| Copies receiver data | Usually yes | No extra struct copy |
Interface implemented by T | Yes, for this method |
Cheat Sheet
Quick rules
- A type implements an interface if its method set contains all required methods.
Thas methods with receiverT.*Thas methods with receiverTand*T.- If an interface requires a method defined on
*T, then only*Timplements that interface.
Fast diagnosis
If you see:
X does not implement Y (... method has a pointer receiver)
check these:
- Is the missing method declared as
func (x *X) Method()? - Are you passing
Xwhere*Xis needed? - Are you assigning a value instead of a pointer to an interface?
- Is your constructor returning
Xwhen it should return*X? - Are mixed receiver types causing confusion?
Common fix
Change this:
y Y = x
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.
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
Loggerinterface with aLog(string)method and aCount() intmethod. - Create a
MemoryLoggerstruct that stores log messages. - Implement
Log(string)so it adds a message to the struct. - Implement
Count() intso it returns the number of stored messages. - Use the logger through the interface and print the final count.
Keep learning
Related questions
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Check if a Value Exists in a Slice in Go
Learn how to check whether a value exists in a slice in Go, and why Go has no Python-style `in` operator for arrays or slices.
Concatenating Slices in Go with append
Learn how to concatenate two slices in Go using append and the ... operator, with examples, pitfalls, and practical usage.