liveBook · Manning

Optional interface

As explained in Go by Example

An optional interface is an interface whose support a type may provide to expose additional behavior without being required by the base contract.

An optional interface is a small interface that a concrete type may implement to expose extra behavior without enlarging the base contract. It lets an API evolve while existing implementations continue to satisfy the original interface. In Go’s HTTP APIs, a value used as http.ResponseWriter may additionally support flushing, server push, or connection hijacking. section 9.4.1 section 10.1

type Flusher interface {
  // Flush sends any buffered data to the client.
  Flush()
}

http.Flusher adds one capability—sending buffered response data immediately—without making Flush part of every ResponseWriter implementation. section 9.4.1

Detecting optional support

Use a type assertion before calling an optional method. If the assertion succeeds, the handler can flush buffered output; if it fails, the handler continues without assuming that flushing exists. This enables features such as streamed downloads while preserving compatibility with ordinary response writers. section 9.4.1

func MyHandler(w http.ResponseWriter, r *http.Request) {
  if flusher, ok := w.(http.Flusher); ok {  #1
    flusher.Flush()  #2
  }
}

The comma-ok assertion separates a supported optional capability from an unsupported one before Flush is called. section 9.4.1

Wrapping complicates optional interfaces. Embedding a ResponseWriter promotes only methods in the ResponseWriter interface; it does not automatically preserve optional methods implemented by the runtime value. A wrapper can therefore make a downstream handler believe Flush is unavailable even when an inner response writer supports it. section 9.4.1 section 9.9

A wrapper chain can bury optional behavior several layers down. An optional Unwrap method can expose the next ResponseWriter, but recursive assertions add complexity. Go 1.20’s concrete http.ResponseController instead searches wrapped response writers for supported operations such as Flush and reports http.ErrNotSupported when none is available. section 9.4.2

func MyHandler(w http.ResponseWriter, r *http.Request) {
  err := http.NewResponseController(w).Flush()  #1
  if errors.Is(err, http.ErrNotSupported) { #2
      . . .
  }
}

ResponseController centralizes traversal and turns unavailable optional behavior into standard error handling. section 9.4.2

Use optional interfaces deliberately:

  • Keep the base interface small and put independently supported features in focused optional interfaces. section 10.1
  • Guard direct optional-interface calls with a type assertion rather than assuming every implementation supports the feature. section 9.4.1
  • Prefer a concrete controller or facade when wrappers require recursive capability discovery or when the API must gain methods safely over time. section 9.4.2 section 10.1

This pattern also appears in database drivers: a concrete API can detect small driver capabilities internally, allowing features such as PingContext without requiring all drivers or callers to change. The result is explicit composition around small contracts rather than a growing, breaking interface. section 10.1 section 9.0

How Go by Example covers it

How deep it goes
Explains assertions, embedding-related method hiding, recursive wrapper lookup, and ResponseController as an alternative.
Examples
Uses http.Flusher and wrapped ResponseWriter chains, then sql/driver Pinger with PingContext.
What it assumes
Assumes familiarity with Go interfaces and introduces optional capability detection through type assertions.
What you can do after
Apply it via type assertions or ResponseController to access optional HTTP capabilities without assuming universal support.
How it connects
Connects type assertions, interface embedding, wrapping, unwrapping, ResponseController, and concrete types.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Go by Example. The section links point to the parts of the book it draws on.