Fundamentals 11 min read

Eliminate Boilerplate: Go’s New Proposal to Convert Functions Directly to Interfaces

The article examines Go’s long‑standing boilerplate adapter pattern, explains the newly accepted Issue #47487 that enables explicit conversion of functions to single‑method interfaces, compares it with the rejected implicit assignability approach, and shows how the compiler generates hidden types to preserve safety and reduce code.

TonyBai
TonyBai
TonyBai
Eliminate Boilerplate: Go’s New Proposal to Convert Functions Directly to Interfaces

Hello, I’m Tony Bai.

In everyday Go development, many developers write repetitive adapter‑style boilerplate when they need a simple read‑only io.Reader implementation, often ending up with double‑wrapping code like io.Reader(ReaderFunc(myFunc)). This "adapter‑type definition" (e.g., http.HandlerFunc in the standard library) works but inflates projects with meaningless boilerplate.

To eliminate this pain, active contributor Merovius submitted Issue #47487, proposing that functions be explicitly convertible to single‑method interfaces. The proposal is active and has a prototype implementation (CL 572835), improving code clarity and subtly tweaking Go’s type system.

Pain Point: Why We Hate Nested Wrapper Code

In complex microservice or system‑level development, we often need to wrap behavior temporarily. For example, to count bytes written, the traditional approach defines a dedicated struct with a Write method, resulting in over ten lines of code and polluting the package namespace with a one‑off type.

type countingWriter struct {
    w io.Writer
    n int64
}

func (w *countingWriter) Write(p []byte) (n int, err error) {
    n, err = w.w.Write(p)
    w.n += int64(n)
    return n, err
}

func main() {
    cw := &countingWriter{w: os.Stdout}
    // write data to cw
    fmt.Println(cw.n, "bytes written")
}

With the new proposal, using closures and function‑to‑interface conversion, the same logic shrinks dramatically:

func main() {
    var N int64
    // Core: directly convert an anonymous function to io.Writer!
    cw := io.Writer(func(p []byte) (n int, err error) {
        n, err = os.Stdout.Write(p)
        N += int64(n)
        return n, err
    })
    // write data to cw
    fmt.Println(N, "bytes written")
}

The code line count is cut in half, and the state variable N stays neatly scoped within the function, eliminating unnecessary struct names and improving cohesion.

Design Trade‑off: Explicit Conversion vs. Implicit Assignment

Earlier, a more aggressive proposal (#21670) suggested allowing implicit assignment of functions to matching single‑method interfaces (Assignability). The Go core team rejected it due to ambiguity and safety concerns. For instance, io.Reader and io.Writer share the same method signature (p []byte) (n int, err error). Implicitly assigning a function would leave the compiler unable to determine whether the function should be treated as a reader or a writer.

To preserve type safety and clear intent, the accepted proposal requires explicit conversion, e.g.:

r := io.Reader(myFunc)
w := io.Writer(myFunc)

Programmers must loudly tell the compiler, “I know the meaning of this function signature and I intend to use it as a Reader/Writer,” thereby avoiding the logical confusion of implicit matching.

Compiler Magic: Handling Reflection and Type Assertions

Only defined types can have methods; unnamed function types have no method set. When a plain function is converted to io.Reader, the compiler generates an unexported synthetic type (e.g., runtime.io_reader.func) that implements the Read method by invoking the supplied function body.

This design offers two key benefits:

Full backward compatibility : existing reflection code continues to work.

Preserves syntactic intuition : the synthetic type is unexported, preventing direct type assertions that could break the clean abstraction.

Standard Library Boilerplate Survey

Merovius scanned the Go standard library and found numerous repetitive adapter definitions:

Massive duplication in test code for temporary io.Reader, io.Writer, and io.Closer function types.

In net/http, two identical‑signature types ( funcWriter and writerFunc) exist in different test files.

To compensate for the lack of native support, the standard library exposed helper types such as net/http.HandlerFunc, cmd/go ActorFunc, and x/mod HashReaderFunc.

If the proposal lands, dozens of such adapter definitions and duplicated code would be eliminated.

Impact on Third‑Party Libraries

Mock frameworks and testing assertion libraries could replace verbose fake implementations with concise anonymous functions, simplifying codebases considerably.

Conclusion: Go’s Pragmatic Evolution

The lengthy discussion around Issue #47487 showcases Go’s careful approach to language evolution. The team rejected unsafe implicit matching and overly complex generic interface literals, opting for an explicit conversion that generates hidden types internally. This conservatism ensures runtime safety and predictability while gradually reducing boilerplate, embodying Go’s engineering aesthetic of “boring but reliable.”

With the prototype CL 572835 maturing, developers can look forward to saying goodbye to repetitive HandlerFunc patterns and enjoying cleaner, more readable Go code.

Discussion: Do you agree with Go’s choice of explicit conversion over implicit assignability? Which single‑method interfaces in your projects could benefit from this new feature?

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CompilerGoLanguage Designtype safetyinterfaceboilerplatefunction conversion
TonyBai
Written by

TonyBai

Tony Bai's tech world (tonybai.com). Not satisfied with just "knowing how", we strive for mastery. Focused on Go language internals, high-quality engineering practices, and cloud‑native architecture, exploring cutting‑edge intersections of Go and AI. Gophers who pursue technology are welcome—follow me and evolve with Go.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.