go-interfaces

Master Go's interface system for creating flexible, decoupled code through implicit implementation and composition patterns.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "go-interfaces" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-go-interfaces

Go Interfaces

Master Go's interface system for creating flexible, decoupled code through implicit implementation and composition patterns.

Basic Interfaces

Defining and implementing interfaces:

package main

import "fmt"

// Define interface type Writer interface { Write(p []byte) (n int, err error) }

// Implement interface (implicit) type ConsoleWriter struct{}

func (cw ConsoleWriter) Write(p []byte) (n int, err error) { fmt.Print(string(p)) return len(p), nil }

func main() { var w Writer = ConsoleWriter{} w.Write([]byte("Hello, World!\n")) }

Multiple methods in interface:

type Reader interface { Read(p []byte) (n int, err error) }

type ReadWriter interface { Read(p []byte) (n int, err error) Write(p []byte) (n int, err error) }

// Implement ReadWriter type File struct { name string }

func (f *File) Read(p []byte) (n int, err error) { // Implementation return 0, nil }

func (f *File) Write(p []byte) (n int, err error) { // Implementation return len(p), nil }

Empty Interface

Using interface{} (any in Go 1.18+):

// Accepts any type func printValue(v interface{}) { fmt.Println(v) }

// Modern syntax (Go 1.18+) func printAny(v any) { fmt.Println(v) }

func main() { printValue(42) printValue("hello") printValue(true)

printAny(3.14)

}

Type assertions:

func processValue(v interface{}) { // Type assertion if str, ok := v.(string); ok { fmt.Println("String:", str) }

// Type switch
switch val := v.(type) {
case int:
    fmt.Println("Integer:", val)
case string:
    fmt.Println("String:", val)
case bool:
    fmt.Println("Boolean:", val)
default:
    fmt.Println("Unknown type")
}

}

Interface Composition

Embedding interfaces:

type Reader interface { Read(p []byte) (n int, err error) }

type Writer interface { Write(p []byte) (n int, err error) }

type Closer interface { Close() error }

// Compose interfaces type ReadWriter interface { Reader Writer }

type ReadWriteCloser interface { Reader Writer Closer }

// Standard library example import "io"

func useReadWriteCloser(rwc io.ReadWriteCloser) { // Can call Read, Write, and Close rwc.Write([]byte("data")) rwc.Close() }

Common Interfaces

Standard library interfaces:

// Stringer interface type Stringer interface { String() string }

type Person struct { Name string Age int }

func (p Person) String() string { return fmt.Sprintf("%s (%d years old)", p.Name, p.Age) }

// error interface type error interface { Error() string }

type MyError struct { Message string }

func (e MyError) Error() string { return e.Message }

// sort.Interface type Interface interface { Len() int Less(i, j int) bool Swap(i, j int) }

type ByAge []Person

func (a ByAge) Len() int { return len(a) } func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

Interface Design Patterns

Small interfaces:

// Good: small, focused interfaces type Getter interface { Get(key string) (value string, exists bool) }

type Setter interface { Set(key, value string) }

type Deleter interface { Delete(key string) }

// Compose as needed type Cache interface { Getter Setter Deleter }

Accept interfaces, return structs:

// Accept interface parameter func processReader(r io.Reader) error { data, err := io.ReadAll(r) if err != nil { return err } fmt.Println(string(data)) return nil }

// Return concrete type func newConfig() *Config { return &Config{ Host: "localhost", Port: 8080, } }

type Config struct { Host string Port int }

Nil Interfaces

Understanding nil interfaces:

func checkNil() { var i interface{} fmt.Println(i == nil) // true

var p *Person
i = p
fmt.Println(i == nil) // false! (type is set, value is nil)

// Proper nil check
v, ok := i.(*Person)
fmt.Println(v == nil, ok) // true, true

}

Interface Satisfaction

Checking interface implementation:

// Compile-time check var _ io.Writer = (*MyWriter)(nil) var _ io.Reader = (*MyReader)(nil)

type MyWriter struct{}

func (w *MyWriter) Write(p []byte) (n int, err error) { return len(p), nil }

// If MyWriter doesn't implement Writer, compilation fails

Duck Typing

Implicit interface satisfaction:

// No explicit "implements" keyword needed type Duck interface { Quack() Walk() }

type RealDuck struct{}

func (d RealDuck) Quack() { fmt.Println("Quack!") }

func (d RealDuck) Walk() { fmt.Println("Waddle waddle") }

type Robot struct{}

func (r Robot) Quack() { fmt.Println("Beep boop quack") }

func (r Robot) Walk() { fmt.Println("mechanical walking sounds") }

func makeDuckDoThings(d Duck) { d.Quack() d.Walk() }

func main() { makeDuckDoThings(RealDuck{}) makeDuckDoThings(Robot{}) }

Polymorphism

Using interfaces for polymorphism:

type Shape interface { Area() float64 Perimeter() float64 }

type Rectangle struct { Width, Height float64 }

func (r Rectangle) Area() float64 { return r.Width * r.Height }

func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) }

type Circle struct { Radius float64 }

func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }

func (c Circle) Perimeter() float64 { return 2 * 3.14159 * c.Radius }

func printShapeInfo(s Shape) { fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter()) }

func main() { shapes := []Shape{ Rectangle{Width: 10, Height: 5}, Circle{Radius: 7}, }

for _, shape := range shapes {
    printShapeInfo(shape)
}

}

Dependency Injection

Using interfaces for testability:

// Define interface for dependency type UserRepository interface { GetUser(id int) (*User, error) SaveUser(user *User) error }

// Production implementation type PostgresUserRepo struct { db *sql.DB }

func (r *PostgresUserRepo) GetUser(id int) (*User, error) { // Database query return &User{}, nil }

func (r *PostgresUserRepo) SaveUser(user *User) error { // Database insert/update return nil }

// Test implementation type MockUserRepo struct { users map[int]*User }

func (m *MockUserRepo) GetUser(id int) (*User, error) { user, exists := m.users[id] if !exists { return nil, errors.New("user not found") } return user, nil }

func (m *MockUserRepo) SaveUser(user *User) error { m.users[user.ID] = user return nil }

// Service depends on interface, not concrete type type UserService struct { repo UserRepository }

func (s *UserService) GetUserName(id int) (string, error) { user, err := s.repo.GetUser(id) if err != nil { return "", err } return user.Name, nil }

type User struct { ID int Name string }

Builder Pattern with Interfaces

Fluent interface pattern:

type QueryBuilder interface { Select(fields ...string) QueryBuilder From(table string) QueryBuilder Where(condition string) QueryBuilder Build() string }

type sqlQueryBuilder struct { selectFields []string fromTable string whereClause string }

func NewQueryBuilder() QueryBuilder { return &sqlQueryBuilder{} }

func (b *sqlQueryBuilder) Select(fields ...string) QueryBuilder { b.selectFields = fields return b }

func (b *sqlQueryBuilder) From(table string) QueryBuilder { b.fromTable = table return b }

func (b *sqlQueryBuilder) Where(condition string) QueryBuilder { b.whereClause = condition return b }

func (b *sqlQueryBuilder) Build() string { query := "SELECT " + strings.Join(b.selectFields, ", ") query += " FROM " + b.fromTable if b.whereClause != "" { query += " WHERE " + b.whereClause } return query }

func main() { query := NewQueryBuilder(). Select("id", "name", "email"). From("users"). Where("age > 18"). Build()

fmt.Println(query)

}

Strategy Pattern

Implementing strategy pattern:

type PaymentStrategy interface { Pay(amount float64) error }

type CreditCardPayment struct { CardNumber string }

func (c *CreditCardPayment) Pay(amount float64) error { fmt.Printf("Paying %.2f with credit card %s\n", amount, c.CardNumber) return nil }

type PayPalPayment struct { Email string }

func (p *PayPalPayment) Pay(amount float64) error { fmt.Printf("Paying %.2f via PayPal to %s\n", amount, p.Email) return nil }

type ShoppingCart struct { paymentMethod PaymentStrategy }

func (cart *ShoppingCart) SetPaymentMethod(pm PaymentStrategy) { cart.paymentMethod = pm }

func (cart *ShoppingCart) Checkout(amount float64) error { return cart.paymentMethod.Pay(amount) }

func main() { cart := &ShoppingCart{}

cart.SetPaymentMethod(&#x26;CreditCardPayment{CardNumber: "1234-5678"})
cart.Checkout(100.00)

cart.SetPaymentMethod(&#x26;PayPalPayment{Email: "user@example.com"})
cart.Checkout(50.00)

}

Adapter Pattern

Adapting interfaces:

// Third-party logger type ThirdPartyLogger struct{}

func (t *ThirdPartyLogger) LogMessage(msg string, level int) { fmt.Printf("[Level %d] %s\n", level, msg) }

// Our application interface type Logger interface { Info(msg string) Error(msg string) }

// Adapter type LoggerAdapter struct { thirdParty *ThirdPartyLogger }

func (a *LoggerAdapter) Info(msg string) { a.thirdParty.LogMessage(msg, 0) }

func (a *LoggerAdapter) Error(msg string) { a.thirdParty.LogMessage(msg, 2) }

func useLogger(logger Logger) { logger.Info("Application started") logger.Error("An error occurred") }

func main() { adapter := &LoggerAdapter{ thirdParty: &ThirdPartyLogger{}, } useLogger(adapter) }

When to Use This Skill

Use go-interfaces when you need to:

  • Define contracts for behavior without implementation

  • Enable polymorphism and code reuse

  • Create testable code with dependency injection

  • Implement design patterns (strategy, adapter, etc.)

  • Build plugin systems or extensible architectures

  • Decouple components in large applications

  • Mock dependencies in tests

  • Follow SOLID principles in Go

  • Create flexible, maintainable APIs

  • Support multiple implementations of same behavior

Best Practices

  • Keep interfaces small and focused (1-3 methods)

  • Accept interfaces, return concrete types

  • Define interfaces where they're used, not implemented

  • Use interface composition for complex interfaces

  • Don't use empty interface unless absolutely necessary

  • Verify interface implementation at compile time

  • Document expected behavior in interface comments

  • Prefer many small interfaces over large ones

  • Use standard library interfaces when applicable

  • Name interfaces with -er suffix (Reader, Writer, etc.)

Common Pitfalls

  • Making interfaces too large or generic

  • Defining unused interfaces "just in case"

  • Returning interfaces instead of concrete types

  • Not checking for nil interface values properly

  • Over-abstracting simple code

  • Forgetting that interfaces are satisfied implicitly

  • Using empty interface excessively

  • Not documenting interface contracts

  • Creating interfaces for single implementation

  • Confusing nil value vs nil interface

Resources

  • Effective Go - Interfaces

  • Go Blog - Laws of Reflection

  • Interface Documentation

  • Go by Example - Interfaces

  • Accept Interfaces, Return Structs

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

typescript-type-system

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

c-systems-programming

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

cpp-templates-metaprogramming

No summary provided by upstream source.

Repository SourceNeeds Review