compare Implicit Satisfaction with an alternative
Fast recall
Quick revision
Core answer
Practical understanding of Implicit Satisfaction.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The comma-ok assertion works for interfaces as well as concrete types:
var x interface{} = File{"b"}
if r, ok := x.(Reader); ok {
fmt.Println(r.Read()) // runs — File satisfies Reader
}The one-assignment form (r := x.(Reader)) panics on failure — the comma-ok form is the graceful probe.
When the question spans several candidates, the type switch:
switch v := x.(type) {
case Reader:
fmt.Println("it reads:", v.Read())
case Closer:
fmt.Println("it closes")
default:
fmt.Println("neither")
}The first case whose method set the dynamic type satisfies wins, and v is bound to that type inside the case.
Where this pattern lives in real code: error handling (errors.As is the modern form), serialization layers (does it implement MarshalJSON?), optional capability interfaces (io.WriterTo as a fast path). The capability-probe idiom — 'if the value can do X, use the fast path' — is the standard library's own style (fmt checks for Formatter, Stringer, error in order).
The failure mode to know: a typed-nil inside an interface can satisfy the assertion (ok true) and then panic on method call through a nil receiver. ok answers 'does the dynamic TYPE satisfy' — it knows nothing about the VALUE's nilness.
compare Implicit Satisfaction with an alternative
Direct Answer
Type assertion with the comma-ok form: v, ok := x.(Reader) — ok is true when x's dynamic type satisfies Reader. Or a type switch over multiple interfaces.
Deep Dive & Architecture
The assertion's mechanics: x.(Reader) reads the interface value's type descriptor, checks Reader's method list against that type's itable, and on success produces a NEW interface value (type and data unchanged, reinterpreted as Reader). The ok form branches instead of panicking; the check is the same method-subset comparison the compiler does at assignment — done at runtime because x's dynamic type is a runtime fact.
The type switch compiles to a sequence of assertions with one optimization: it reads x's type descriptor ONCE, then compares against each case's type (concrete cases: pointer comparison; interface cases: itable lookup). The first match binds v — and v's static type per-case is the case's type, so the case body gets a typed value with full method access.
The capability-probe pattern's runtime cost: each probe is a type-descriptor comparison — nanoseconds. That's why fmt's ordered checking (Formatter then Stringer then error) is viable in a formatting hot path: three nearly-free probes choosing the richest behavior present. The pattern generalizes to any layered API: try the specialized interface, fall back to the general one.
The nil-dynamic-value subtlety, closed: ok is a statement about the TYPE fit. The VALUE can be a typed nil — the assertion succeeds, methods dispatch, and a pointer-receiver method that dereferences panics. The complete pattern: assert, THEN nil-check the concrete value — or design methods that tolerate nil receivers.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Implicit Satisfaction, and when would you use it?
- When would you use Implicit Satisfaction in a real project?
- What is a common mistake when using Implicit Satisfaction?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.