Go
Beginner
1 min read
Interface Composition and nil Interfaces
Example
package main
import (
"fmt"
"io"
"strings"
)
// Composed interface
type ReadWriteCloser interface {
io.Reader
io.Writer
io.Closer
}
// nopCloser wraps a ReadWriter and adds a no-op Close method
type nopCloser struct{ io.ReadWriter }
func (nopCloser) Close() error { return nil }
func NewReadWriteCloser(rw io.ReadWriter) ReadWriteCloser {
return nopCloser{rw}
}
// Nil interface pitfall demonstration
type MyError struct{ msg string }
func (e *MyError) Error() string { return e.msg }
func riskyFunc(fail bool) error {
var err *MyError // typed nil pointer
if fail {
err = &MyError{"something went wrong"}
}
// BUG: returning a typed nil pointer as error is NOT nil!
// return err ← do NOT do this
if err != nil {
return err
}
return nil // correct: return untyped nil
}
func main() {
// Interface composition
buf := strings.NewReader("data to process")
var sb strings.Builder
rw := struct {
io.Reader
io.Writer
}{buf, &sb}
rwc := NewReadWriteCloser(rw)
io.Copy(rwc, rwc.Reader.(io.Reader))
defer rwc.Close()
fmt.Println("buffer:", sb.String())
// Nil interface
err := riskyFunc(false)
fmt.Println("err is nil:", err == nil) // true
err = riskyFunc(true)
fmt.Println("err is nil:", err == nil) // false
fmt.Println("error:", err)
}