Fix

"X does not implement Y (missing method Z)" in Go

Fix the Go interface error by adding the missing method to your struct with the correct signature.

The method is right there. The compiler disagrees.

You define an interface. You build a struct. You add the methods. You pass the struct to a function that expects the interface. The build fails.

MyStruct does not implement MyInterface (missing method DoThing)

You scroll up. DoThing is right there. The name matches. The parameters look correct. The compiler must be broken. It isn't. Go checks interfaces with literal precision. The signature is off by a single character, the receiver type is wrong, or the method set doesn't include what you think it does. This error is the compiler telling you that the shape of your struct does not match the shape of the interface. Fix the shape, and the error vanishes.

Interfaces are contracts, not classes

Go uses structural typing. You never declare that a type implements an interface. There is no implements keyword. You simply define the methods. If your type has the methods with the exact signatures required by the interface, it satisfies the interface. The compiler checks this automatically.

Think of an interface as a power outlet. The outlet has two slots and a ground pin. The struct is the plug. The outlet does not care what the plug is made of, who manufactured it, or what it looks like. It only cares about the shape. If the plug has two prongs and a ground in the exact right positions, it fits. If one prong is slightly too wide, or if the ground is missing, it does not fit. The outlet rejects the plug without hesitation.

Go interfaces work the same way. The interface defines the shape. The struct provides the shape. The compiler compares them. If they match, the code compiles. If they do not, you get the error.

Minimal example: signature mismatch

The most common cause of this error is a signature mismatch. The method name, parameter types, and return types must match the interface definition exactly. Go does not perform type coercion for interface satisfaction.

package main

import "fmt"

// Logger defines the contract for logging messages.
type Logger interface {
	// Log accepts a string message and returns nothing.
	Log(msg string)
}

// ConsoleLogger wants to satisfy Logger.
type ConsoleLogger struct{}

// Log implements the Logger interface.
// The signature must match exactly: receiver, name, params, returns.
func (c ConsoleLogger) Log(msg string) {
	fmt.Println(msg)
}

// BrokenLogger has a method that looks similar but fails the contract.
type BrokenLogger struct{}

// Log returns an error, which violates the Logger interface.
// The interface requires no return value.
func (b BrokenLogger) Log(msg string) error {
	return nil
}

func main() {
	var l Logger

	// ConsoleLogger satisfies Logger. The signatures match.
	l = ConsoleLogger{}
	l.Log("works")

	// BrokenLogger does not satisfy Logger.
	// The compiler rejects this with:
	// cannot use BrokenLogger{} (value of type BrokenLogger) as Logger value in assignment:
	// BrokenLogger does not implement Logger (wrong type for method Log)
	// 	have Log(string) error
	// 	want Log(string)
	// l = BrokenLogger{}
}

The compiler error tells you exactly what went wrong. It shows what you have and what it wants. Read the error message. It is precise.

Method sets and the pointer trap

The second most common cause is the receiver type. Go distinguishes between value receivers and pointer receivers. This distinction determines the "method set" of a type. The method set is the collection of methods available for a type. The compiler uses the method set to check interface satisfaction.

For a type T, the method set includes all methods with receiver type T. For a type *T, the method set includes all methods with receiver type T and all methods with receiver type *T.

This means a pointer type often satisfies more interfaces than the value type. If an interface requires a method with a pointer receiver, only the pointer type satisfies the interface. The value type does not.

package main

// Counter holds a number.
type Counter struct {
	n int
}

// Inc increments the counter.
// It uses a pointer receiver because it modifies the struct.
func (c *Counter) Inc() {
	c.n++
}

// Increments is an interface that requires Inc.
type Increments interface {
	Inc()
}

func main() {
	var c Counter

	// c is a value of type Counter.
	// The method set of Counter does not include Inc.
	// Inc has a pointer receiver, so it belongs to the method set of *Counter.
	// The compiler rejects this with:
	// cannot use c (variable of type Counter) as Increments value in variable declaration:
	// Counter does not implement Increments (missing method Inc)
	// var i Increments = c

	// &c is a pointer of type *Counter.
	// The method set of *Counter includes Inc.
	// This assignment succeeds.
	var i Increments = &c
	i.Inc()
}

If you see the "missing method" error and the method is clearly defined, check the receiver. If the method has a pointer receiver, you must pass a pointer to the interface. If the method has a value receiver, you can pass either a value or a pointer.

Convention aside: receiver names should be short, usually one or two letters matching the type. Use (c *Counter), not (this *Counter) or (self *Counter). This keeps code readable and follows the style of the standard library.

Realistic example: HTTP handlers

The net/http package uses interfaces extensively. http.Handler is the core interface for handling requests. It requires a single method: ServeHTTP(http.ResponseWriter, *http.Request).

Developers often run into this error when building custom handlers. The mistake is usually in the method signature. The parameters must be exactly http.ResponseWriter and *http.Request. The method must return nothing.

package main

import (
	"fmt"
	"net/http"
)

// MyHandler wants to satisfy http.Handler.
type MyHandler struct{}

// ServeHTTP implements http.Handler.
// The signature must match exactly.
// Parameters: http.ResponseWriter and *http.Request.
// Returns: nothing.
func (h MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello from MyHandler")
}

// BrokenHandler has a subtle signature error.
type BrokenHandler struct{}

// ServeHTTP returns an error, which violates http.Handler.
// http.Handler requires no return value.
func (h BrokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
	return nil
}

func main() {
	// MyHandler satisfies http.Handler.
	var handler http.Handler = MyHandler{}
	_ = handler

	// BrokenHandler does not satisfy http.Handler.
	// The compiler rejects this with:
	// cannot use BrokenHandler{} (value of type BrokenHandler) as http.Handler value in variable declaration:
	// BrokenHandler does not implement http.Handler (wrong type for method ServeHTTP)
	// 	have ServeHTTP(http.ResponseWriter, *http.Request) error
	// 	want ServeHTTP(http.ResponseWriter, *http.Request)
	// var handler http.Handler = BrokenHandler{}
}

The error message shows the mismatch. have shows what you wrote. want shows what the interface requires. Fix the signature to match want.

Pitfalls and compiler errors

Beyond signature mismatches and receiver traps, a few other patterns trigger this error.

Typos in method names. Go is case-sensitive. Log is not the same as log. If the interface requires Log, the method must be Log. The compiler error will list the missing method. If you defined log instead of Log, the compiler sees Log as missing.

Unexported methods. Interface methods must be exported if the interface is exported. If you define an interface in one package and try to satisfy it in another, the methods must start with a capital letter. If the method is lowercase, it is unexported and cannot be seen by other packages. The compiler error will still say the method is missing.

Equivalent types are not the same. Go requires exact type matches. If the interface requires error, you cannot return *MyError even if MyError implements error. The return type must be error. If the interface requires []byte, you cannot return []rune. The types must be identical.

The compiler error for a missing method is straightforward. X does not implement Y (missing method Z). If the method exists but has the wrong signature, the error is more detailed. X does not implement Y (wrong type for method Z). It shows the have and want signatures. Use this information to fix the code.

Convention aside: the standard library interface io.Writer is the gold standard for interface design. It has a single method Write(p []byte) (int, error). Many types satisfy io.Writer. os.File, bytes.Buffer, and http.ResponseWriter all implement it. This allows you to write code that works with any writer. Design your interfaces to be small and focused. Small interfaces are easier to satisfy and easier to test.

Decision matrix

Use an interface when you want to decouple code from specific implementations. Interfaces allow you to swap implementations without changing the calling code. This is useful for testing, where you can inject a mock implementation. It is also useful for libraries, where you want to accept any type that provides certain behavior.

Use a struct when you need to hold state and define concrete behavior. Structs are the building blocks of your application. They hold data and provide methods to manipulate that data.

Use a pointer receiver when the method modifies the struct fields. If the method changes the state of the struct, it must use a pointer receiver. Otherwise, the method operates on a copy, and the changes are lost.

Use a value receiver when the method reads data and the struct is small enough to copy cheaply. Value receivers allow the method to work with both values and pointers. This provides flexibility for callers.

Use a type alias when you need to add methods to an existing type without wrapping it. A type alias creates a new type with the same underlying type. You can define methods on the alias. This is useful when you want to add behavior to a type from another package.

Where to go next

Interfaces are contracts. Signatures are the fine print. Read the fine print. The method set determines the fit. Check the receiver. Go checks interfaces at compile time. You get the error now, not in production. Trust the compiler. Fix the shape.