Represent and transform data safely
Go Interfaces and Pointer Receivers: Does the Struct or Its Pointer Implement It?
Learn why *Email satisfies a Go interface when Email does not, with a complete example and compile-time assertions.

If an interface requires a method declared on *Email, pass *Email, not Email, to a function accepting that interface. The distinction is about method sets: the pointer-receiver method belongs to the pointer type’s method set, not the struct value type’s. Go checks that distinction when you pass the argument; you do not need to declare that Email implements anything.
The struct holds data; the interface asks for behavior
A struct such as Email gives a concrete value fields and methods. An interface such as Notifier describes what its caller needs: in this case, a Notify() string method. A function accepting Notifier can use that method without knowing whether the notification comes from an email or another concrete type.
That is the useful reason to introduce an interface here. The caller depends on one operation rather than on Email’s fields. A different type can provide the same operation without changing the caller. Go checks satisfaction implicitly: the relevant method set must include every method the interface lists. See the method-set explanation at Go101 and the interface satisfaction rule at golangtutorial.com.
A complete pointer-receiver example
This program defines an Email whose Notify method has a pointer receiver. It also defines a Console notification to show that send is not tied to one struct:
package main
import "fmt"
type Notifier interface {
Notify() string
}
type Email struct {
Address string
}
func (e *Email) Notify() string {
return "email to " + e.Address
}
type Console struct {
Message string
}
func (c Console) Notify() string {
return c.Message
}
func send(n Notifier) {
fmt.Println(n.Notify())
}
var _ Notifier = (*Email)(nil)
// var _ Notifier = Email{} // Does not compile: Email lacks Notify in its method set.
var _ Notifier = Console{}
func main() {
email := Email{Address: "[email protected]"}
send(&email)
send(Console{Message: "console notification"})
}
The program prints:
email to [email protected]
console notification
Inside send, the expression n.Notify() invokes the method of the concrete value held by n. With &email, that is (*Email).Notify; with the Console value, it is Console.Notify. This is the behavior an interface-typed parameter provides: one call site, with behavior determined by the concrete value passed in. Go101 describes this dispatch in terms of the interface value’s dynamic value.
Why &email matters
The declaration func (e *Email) Notify() string puts Notify in the method set of *Email, not Email. Because Notifier requires Notify() string, *Email satisfies Notifier and Email does not. Passing send(email) in this example would therefore be a compile-time error; passing send(&email) supplies the type whose method set has the required method. The pointer-receiver rule is stated directly here.
This is easy to miss when looking only at the struct declaration. The fields tell you what an Email contains, but interface satisfaction depends on methods and their signatures—including which receiver type owns a required method. When a function parameter is an interface, check the methods on the type you are actually passing: Email or *Email.
Make the expectation explicit at compile time
The line var _ Notifier = (*Email)(nil) is an assertion of type compatibility. The blank identifier discards the value; the assignment asks the compiler whether *Email satisfies Notifier. The commented-out Email{} assignment asks the corresponding question about the value type. Uncomment it and compilation fails because Email’s method set lacks the required pointer-receiver method.
Do not mistake the nil pointer in that assertion for a notification you should pass to send. It is used only in the discarded assignment. A nil pointer stored in an interface is not a nil interface, and this example’s Notify method reads a field through its receiver. The nil-interface distinction is explained here.
Keep the interface focused on what send calls. Here, one method is enough; Email remains a concrete type, while Notifier lets the consumer accept any suitable implementation. When deciding what to pass, the practical check is specific: if the required method has a *Email receiver, pass &email or another *Email, not an Email value.


Comments
No comments yet.