How does a Go type come to satisfy an interface like io.Writer with no implements keyword?
answer
- no keyword joins the two
- the method set is the contract
- checked when used, not when declared
- bytes.Buffer never names io.Writer
basics
~20 sA Go type satisfies an interface just by having every method the interface lists, with matching names and signatures. Nothing is declared anywhere: the compiler checks the match at the point where the value is assigned to the interface.
solid answer
~50 sGo interface satisfaction is implicit and structural. `io.Writer` lists one method, `Write(p []byte) (n int, err error)`. Any type that declares a method named `Write` with exactly that parameter and result types is an `io.Writer` — it never mentions `io`, imports nothing extra, and there is no `implements` clause to write. The compiler performs the check at the places where a concrete value is used as the interface: an assignment, a function argument, a return, or a conversion. If a method is missing or its signature differs, the build fails at that use site with an error saying the type does not implement the interface and naming the offending method. Extra methods are irrelevant, and one type routinely satisfies several interfaces at once without saying so — `*os.File` is an `io.Reader`, an `io.Writer` and an `io.Closer` simply because it has those methods.
code
go · 12 linestype countingWriter struct{ n int }
func (w *countingWriter) Write(p []byte) (int, error) {
w.n += len(p)
return len(p), nil
}
// io.Copy wants an io.Writer; *countingWriter fits because of its
// Write method alone.
func drain(w *countingWriter, r io.Reader) (int64, error) {
return io.Copy(w, r)
}go deeper
Be ready to state the rule in one sentence: a type satisfies an interface by having all of its methods with matching signatures, and there is no implements keyword. Have one example ready, such as a type with a Write method being usable as an io.Writer.
Explain that the compiler performs the check at the use site — an assignment, argument, return or conversion — not at the type declaration, and that method sets are compared by name and exact signature. Mention that embedded fields promote methods into the method set.
Show that you know what the missing declaration costs: nothing in the implementing package records the relationship, so satisfaction can be lost by a refactor without that package's build failing. Be able to say how you would make the relationship explicit.
Frame implicit satisfaction as a dependency-direction choice: abstractions can be introduced after the fact and over code you do not own, which is why Go codebases can defer interfaces. The price is that a satisfied interface becomes an undocumented part of an exported type's contract.
## The rule In Go there is no `implements` keyword, no `: Base` syntax, and no annotation that ties a concrete type to an interface. A type satisfies an interface **if and only if its method set contains every method the interface declares, with the same name and an identical signature**. That is the whole rule. Because the relationship is derived from the shape of the type rather than from a declaration, this style is called *structural* (or *implicit*) satisfaction. The canonical shape is `io.Writer`: ```go type Writer interface { Write(p []byte) (n int, err error) } ``` A type becomes a `Writer` by declaring: ```go func (b *Blob) Write(p []byte) (int, error) { ... } ``` Parameter names do not matter — `p` versus anything else, and the named results `n` and `err` in the interface declaration are documentation only. What must match is the **types**: one `[]byte` parameter, results `(int, error)`, no variadic difference, no extra parameter. ## What "identical signature" excludes Several near-misses do **not** satisfy `io.Writer`, and each produces a compile error rather than a silent fallback: - `Write(p []byte) error` — wrong result list. - `Write(p string) (int, error)` — wrong parameter type; Go has no implicit conversions here. - `write(p []byte) (int, error)` — a lowercase name is a different method name entirely, and an unexported method can only be satisfied from inside the declaring package. - A method promoted from an embedded field **does** count: if `type Logged struct { io.Writer }` embeds a `Writer`, the outer struct has `Write` in its method set and satisfies `io.Writer` too. Extra methods never hurt. A type with fifteen methods satisfies any interface whose methods are a subset of those fifteen. ## When the check happens Satisfaction is checked **at compile time, at the point of use**. There is no registry populated at `init`, no reflection, no runtime scan. The compiler checks the method set when you: - assign: `var w io.Writer = &Blob{}` - pass an argument: `io.Copy(&Blob{}, r)` where `io.Copy` takes an `io.Writer` - return: a function whose result type is `io.Writer` returning `*Blob` - convert: `io.Writer(&Blob{})` If none of those ever happen, the compiler is never asked the question, and the relationship simply is not part of the build. That is the one practical consequence people are caught by: a package can stop satisfying an interface without its own build noticing. When the check does fail, the error names the missing or mismatched method: ``` cannot use (*Blob)(nil) (value of type *Blob) as io.Writer value in variable declaration: *Blob does not implement io.Writer (missing method Write) ``` ## Why the language is built this way Nominal typing — Java, C#, PHP — requires the *implementer* to name the interface. That forces a dependency edge from the implementation toward the abstraction, and it means an abstraction can only be introduced with the implementer's cooperation. Implicit satisfaction inverts that. A package can define an interface over methods that already exist on types it does not own and cannot change, including standard-library types and third-party ones. `bytes.Buffer`, `strings.Builder`, `os.Stdout` and `net/http`'s response writer all satisfy `io.Writer` without any of them referring to each other. It also means an interface can be added, split, or narrowed after the fact without touching a single implementation. `io.Reader` was not "implemented by" `os.File` in any recorded sense; the two simply fit. ## The cost The trade-off is that nothing is written down. The compiler will never tell you "this type is meant to be an `io.Writer`", because that intent lives only in your head and your documentation. Accidental satisfaction is possible too: a type with a `String() string` method satisfies `fmt.Stringer` whether or not you intended it, and `fmt` will then use it when printing. Both directions — losing satisfaction silently, and gaining it accidentally — follow from the same rule, and both are the reason Go codebases use an explicit compile-time assertion when the relationship matters. ## What to say in an interview State the rule (method set contains every declared method with matching signatures), say the check is a compile-time one performed at the use site, and give one concrete pair — `*bytes.Buffer` and `io.Writer` is the safest — noting that `bytes` never mentions `io.Writer`.
- Does a type's package have to import the package that declares the interface it satisfies?No. The implementing type never refers to the interface, so no import is needed. `bytes` does not import `io` on account of `bytes.Buffer` being an `io.Writer`; the only import required is in whichever package actually writes `io.Writer` as a type.
- Can one Go type satisfy several interfaces at the same time without declaring any of them?Yes, and it is normal. `*os.File` has `Read`, `Write`, `Close` and more, so it satisfies `io.Reader`, `io.Writer`, `io.Closer` and every combination of them. Extra methods beyond an interface's list never prevent satisfaction.
- What happens if a type accidentally matches an interface you never intended it to satisfy?It satisfies it, with real consequences. A `String() string` method makes a type a `fmt.Stringer`, so `fmt.Println` will call it instead of printing the struct fields. Structural matching cannot distinguish intent from coincidence, which is why method names in Go carry conventional meaning.
saying these in an interview costs you the question
- Says a Go struct needs an implements clause
- Thinks satisfaction is resolved at run time by reflection
- Believes the implementing package must import the interface's package
- Claims extra methods break interface satisfaction
- Matches only the method name and ignores the signature