skip to content

What does `var _ Backend = (*fileBackend)(nil)` assert, and how does the value-typed form differ?

level: middleimportance: nice to knowfreq 38%

answer

  1. a variable nobody can name
  2. assignment to an interface is the check
  3. nothing is allocated and nothing is called
  4. the value form claims more
  5. it fails where the methods live

basics

~20 s

It is a compile-time assertion that *fileBackend implements Backend: a blank-identifier variable of the interface type is initialised with a typed nil pointer, so the package fails to build if a method is missing or declared on the wrong receiver.

solid answer

~50 s

The declaration creates a package-level variable named `_` of interface type `Backend` and initialises it with a typed nil `*fileBackend`. Assignment to an interface triggers the method-set check, so if `*fileBackend` ever stops implementing `Backend` the package stops compiling - right there, next to the type, instead of at some distant call site. A typed nil is used because nothing is allocated and no method is ever called, so there is nothing to dereference; the blank identifier means no variable is retained. Writing `var _ Backend = fileBackend{}` instead asserts something stronger: that the **value** type satisfies the interface, which holds only if every one of the interface's methods is declared with a value receiver. Pick the pointer form for the usual case, and the value form deliberately when copies must satisfy the interface too.

code

go · 17 lines
go
type Backend interface {
	Close() error
}

type fileBackend struct {
	path string
}

func (b *fileBackend) Close() error {
	return nil
}

// Compiles: *fileBackend's method set holds Close.
var _ Backend = (*fileBackend)(nil)

// Would not compile: fileBackend's method set does not hold Close.
// var _ Backend = fileBackend{}

go deeper

for a junior

Recognise the line when you meet it in a codebase: it is a free compile-time check that a type implements an interface, not a variable anyone uses at run time.

for a middle

Explain the mechanics - assignment to an interface type is what runs the method-set check, the typed nil avoids any allocation, and the value form asserts the stronger claim about value receivers.

for a senior

Use placement deliberately so a broken implementation fails in the package that owns the methods, and know when a constructor returning the interface already gives you the same guarantee.

for a principal

Treat it as cheap documentation of an intended contract: it records which interfaces a type is meant to satisfy so a later refactor cannot quietly narrow what other teams can rely on.

## What the declaration is ```go var _ Backend = (*fileBackend)(nil) ``` Read it piece by piece: - `var _` declares a variable whose name is the blank identifier. Nothing is bound; the compiler checks the declaration and keeps no variable around, so there is no unused-variable complaint and nothing to reference later. - `Backend` is the declared type, so this is an assignment of the initialiser **to an interface type**. - `(*fileBackend)(nil)` is a conversion of the untyped `nil` to the pointer type `*fileBackend` - a typed nil pointer. Assigning a concrete type to an interface is exactly the operation that triggers the method-set check. If `*fileBackend`'s method set does not contain every method `Backend` lists, the package does not compile. Nothing else happens: no allocation, no initialisation, no call. ## Why a nil pointer and not a real value `var _ Backend = &fileBackend{}` would work identically for the check, but it allocates a struct at package initialisation and keeps it alive as long as the interface value exists. The typed nil costs nothing. It is safe because **no method is called on it** - a nil pointer only panics when something dereferences it, and this declaration never does. (Go is quite happy for a nil `*T` to sit in an interface; that interface value is not itself nil, which is a separate trap worth knowing but not what is happening here.) ## What it buys you Without the assertion, the fact that `*fileBackend` implements `Backend` is only checked where a `*fileBackend` is actually assigned to a `Backend` - a registry file, a constructor, a function call, sometimes in another package entirely. Delete a method or change a receiver from `*fileBackend` to `fileBackend`, and the error appears somewhere you were not editing, phrased in terms of a type you were not thinking about. Put the assertion in the file that declares the methods and the failure lands under your cursor, naming the method and the receiver form directly. It also documents intent: a reader sees at a glance which interfaces this type is meant to satisfy, which is not otherwise written down anywhere in Go - implementation is structural and implicit. The conventional placement is immediately after the type declaration or just above its methods, and the conventional comment is none at all, because the line is idiomatic enough to read on sight. ## The value-typed form says something else ```go var _ Backend = fileBackend{} ``` This asserts that the **value** type satisfies `Backend`. Because a value's method set holds only methods declared with a value receiver, this compiles only if every method `Backend` lists is declared as `func (b fileBackend) ...`. So the two forms are not interchangeable: - The pointer form is the weaker, usual claim: the pointer works, callers must pass an address. - The value form is the stronger, deliberate claim: copies satisfy the interface too, so the type can be stored by value in a slice or a map and still be used as a `Backend`, and the zero value is usable. If the value form compiles, the pointer form necessarily compiles as well, since `*T`'s method set is a superset of `T`'s. The reverse does not hold. That asymmetry makes the pair a useful diagnostic: if `var _ Backend = fileBackend{}` fails while the pointer form succeeds, you have just proved that at least one interface method is declared with a pointer receiver. ## Variations you will see - `var _ Backend = (*fileBackend)(nil)` - the standard form, used throughout the standard library. - `var _, _ Backend = (*fileBackend)(nil), (*memBackend)(nil)` - several implementations in one declaration; readable enough, though separate lines are more common. - A test that assigns to an interface variable achieves the same check, but only when the tests are compiled, and it is easier to delete by accident. ## When not to bother If the package itself already assigns the type to the interface in a constructor - `func New() Backend { return &fileBackend{} }` - the check is already there, in a line that has a reason to exist, and the assertion adds nothing. The assertion earns its place when the concrete type is handed out concretely and only some other package ever puts it in an interface.

  • Why use a typed nil pointer rather than &fileBackend{} in the assertion?
    Both trigger the same method-set check, but the typed nil allocates nothing and initialises nothing. It is safe because no method is ever called on it - a nil pointer only misbehaves when something dereferences it, and this declaration merely stores it in an interface value that no code reads.
  • Where should the assertion live, and why does the placement matter?
    In the package that declares the concrete type, next to the type or above its methods. That way an edit to a receiver or a deleted method breaks the build in the file being edited, rather than in a distant registry or another package that happens to be the only place the type is assigned to the interface.
  • What does the value-typed assertion guarantee that the pointer form does not?
    That every method the interface lists is declared with a value receiver, so copies of the type satisfy the interface too - it can be stored by value, ranged over, or held in a map element and still used as the interface. It is a stronger and sometimes deliberate promise; if it compiles, the pointer form necessarily compiles as well.
  • When is the assertion redundant?
    When the package already assigns the type to the interface somewhere real - most often a constructor declared to return the interface. That line performs the same check for a reason that is not merely documentation, so an extra assertion adds noise. The assertion pays off when the type is only ever handed out concretely.

saying these in an interview costs you the question

  • Thinks the declaration panics on a nil dereference
  • Believes it allocates or initialises something at startup
  • Says the pointer and value forms assert the same thing
  • Calls it a run-time registration of the type
  • Cannot say why the blank identifier is used