October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

Go Method Sets: What Embedding and Interface Satisfaction Mean at API Boundaries

Go interface satisfaction is determined by method sets, not merely by which methods an expression can call. See how pointer receivers, embedding, and generic constraints change the result.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

In Go, interface satisfaction depends on the method set of a type, not just on which methods you can call through a particular expression. That is why *T may satisfy an interface that T does not, and why embedding T versus *T can change which interfaces a struct satisfies.

Why does *T satisfy an interface but T doesn’t?

A defined type T has the methods declared with receiver T in its method set. The method set of *T includes methods declared with receiver T and methods declared with receiver *T. Interface implementation is based on those method sets: the required method names and signatures must be present. The Go specification’s method-set and interface rules define this behavior.

As an Amazon Associate I earn from qualifying purchases.

For example, Flusher requires a method with a pointer receiver:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
type Flusher interface {
    Flush()
}

type Buffer struct{}

func (b *Buffer) Flush() {}

var _ Flusher = (*Buffer)(nil) // satisfies Flusher

The assignment assertion uses a nil pointer only to check the type relationship at compile time; it does not call Flush. A value of type Buffer does not satisfy Flusher, because Flush is not in Buffer’s method set:

var _ Flusher = Buffer{} // compile-time error

Go does not require a type to declare that it implements an interface. If its method set has the required methods with matching signatures, it implements that interface implicitly. For a basic interface, its type set consists of the non-interface types that implement its listed methods.

Why can I call a pointer receiver on a value, but still get an interface assignment error?

Go permits a convenient method call on an addressable value: if x is addressable and (*T) has method M, then x.M() can act like (&x).M(). This call shorthand does not add M to the method set of T. The Go MethodSets wiki explains the distinction between method calls and method sets.

That difference matters at an interface boundary. A local variable may be addressable, but passing it as an interface value does not change its static type to a pointer:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
type Worker struct{}

func (w *Worker) Run() {}

type Runner interface {
    Run()
}

func start(r Runner) {}

var w Worker
w.Run()      // allowed: shorthand for (&w).Run()
start(w)     // compile-time error: Worker lacks Run in its method set
start(&w)    // allowed: *Worker has Run in its method set

When diagnosing an assignment or argument error, check the type of the value being passed—not only whether the method call works on the expression where it appears.

What methods does an embedded type promote?

An embedded field promotes methods into the method sets of its enclosing struct, but the result depends on whether the field is T or *T. Check the method sets of both the struct type S and its pointer type *S.

Embedded field in S Promoted methods in S Promoted methods in *S
T Methods with receiver T Methods with receiver T or *T
*T Methods with receiver T or *T Methods with receiver T or *T

For a value embedding, a pointer-receiver method promoted from T is available through *S, but not in the method set of S. With a pointer embedding, both receiver kinds are promoted into the method sets of S and *S.

type T struct{}

func (T) ValueMethod()  {}
func (*T) PointerMethod() {}

type ByValue struct { T }
type ByPointer struct { *T }

var _ interface{ ValueMethod() } = ByValue{}
var _ interface{ PointerMethod() } = (*ByValue)(nil)
var _ interface{ PointerMethod() } = ByValue{} // compile-time error

var _ interface{ PointerMethod() } = ByPointer{}

Promotion also makes methods available through selectors, but conflicting or ambiguous promoted selectors cannot be used. Consult the specification’s struct embedding rules when an embedded field or another method introduces a name collision.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does embedding an interface make my type implement it?

Embedding a type or interface is a composition mechanism, not inheritance. An embedded interface contributes its method requirements to the enclosing interface. A concrete struct satisfies that resulting interface only if its method set has every required method.

For example, an interface that embeds Reader and Writer requires both sets of methods. Embedding a field in a struct can promote methods that help meet those requirements, but the struct’s method set still determines whether it satisfies the interface. Effective Go’s bufio.ReadWriter example shows composition through embedded reader and writer implementations.

Interface embedding therefore combines contracts; it does not automatically make an unrelated concrete type implement them. If a struct embeds an interface field, its promoted methods derive from that field’s interface method set, but a nil interface field can still cause a panic if a promoted method is invoked before the field holds a concrete value.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What changed about interfaces with Go generics?

Since Go 1.18, interfaces can describe type sets as well as method requirements. A basic interface—one described by methods and eligible for use as a value type—continues to serve as the familiar runtime interface contract. General interfaces can also include type terms such as ~int, unions, and embedded constraints. A non-basic interface is a constraint, not a type for an ordinary variable or struct field. These rules are specified in the Go specification’s interface and constraint sections.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
type Stringish interface {
    ~string
}

func Echo[T Stringish](value T) T {
    return value
}

// var x Stringish // invalid: Stringish is a constraint, not a value type

The constraint Stringish admits types whose underlying type is string. It does not mean that a value can be stored in a variable of interface type Stringish. Keep these two questions separate: whether a type implements a basic interface, and whether a type argument satisfies a generic constraint.

Why does comparable need a special qualification?

Since Go 1.20, constraint satisfaction has a special case for comparable. A type argument may satisfy a constraint containing comparable even when it does not strictly implement the comparable interface. The specification includes any as a type that can satisfy comparable in a constraint context under this rule. Consequently, “satisfies a constraint” and “implements an interface” are not interchangeable descriptions in every generic type-checking situation.

How should I check an interface boundary?

  1. Identify the exact type at the boundary. Is the caller passing T, *T, or a composite type such as S?
  2. Compare its method set with the interface. Check receiver types and exact signatures; do not infer satisfaction from a successful method call.
  3. For embedding, inspect both S and *S. Determine whether the embedded field is T or *T, then account for promoted methods and any selector ambiguity.
  4. For generics, classify the interface. If it has type terms or other non-basic constraint features, use it as a constraint rather than as an ordinary value type.
  5. Use a compile-time assertion when it clarifies intent. For example, var _ Runner = (*Worker)(nil) records that the pointer type must continue to satisfy Runner. For programmatic inspection of Go types and interfaces, see the go/types package documentation.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.