Go🚀Advancedadvanced
Interface Composition
Go · Advanced · Run with go run
Explanation
Go interfaces can embed other interfaces, building larger contracts from smaller ones. The standard library uses this heavily: `io.ReadWriter` embeds both `io.Reader` and `io.Writer`; `io.ReadWriteCloser` adds `io.Closer`. This compositional approach lets you accept exactly the capabilities you need — nothing more, nothing less.
Real-World Scenario
A compression library accepts `io.ReadWriter` — it needs both read and write on the same stream. A network conn (`net.Conn`) satisfies this; so does a `bytes.Buffer`. No inheritance hierarchy required.
Quick Snippet
snippet.go
type Reader interface { Read([]byte) (int, error) }
type Writer interface { Write([]byte) (int, error) }
type Closer interface { Close() error }
// Composed interfaces
type ReadWriter interface {
Reader
Writer
}
type ReadWriteCloser interface {
Reader
Writer
Closer
}
// Accept minimum required capability
func compress(rw io.ReadWriter) error {
// reads uncompressed, writes compressed to the same conn
}
// Custom composed interface for a specific domain
type Repository interface {
Querier
Mutator
io.Closer
}Full Example
interface_composition.go
// Compile: go run interface_composition.go
// Output:
// wrote 5 bytes
// read back: hello
// closed
package main
import (
"bytes"
"fmt"
"io"
)
// Small interfaces compose into larger contracts
type Reader interface{ Read([]byte) (int, error) }
type Writer interface{ Write([]byte) (int, error) }
type Closer interface{ Close() error }
type ReadWriteCloser interface {
Reader
Writer
Closer
}
// buffer satisfies ReadWriteCloser
type buffer struct {
bytes.Buffer
}
func (b *buffer) Close() error {
fmt.Println("closed")
return nil
}
func useStream(rwc ReadWriteCloser) {
n, _ := rwc.Write([]byte("hello"))
fmt.Println("wrote", n, "bytes")
data, _ := io.ReadAll(rwc)
fmt.Println("read back:", string(data))
rwc.Close()
}
func main() {
useStream(&buffer{})
}
Run command
go run interface_composition.go