# Golang Structs Interfaces > Patrones de diseño de structs e interfaces en Golang: composición, embedding, aserciones de tipo, interfaces pequeñas, DI vía interfaces, tags de campo y receptores puntero vs valor. Fuente: https://skillsagentes.com/skills/samber/cc-skills-golang/golang-structs-interfaces Markdown: https://skillsagentes.com/skills/samber/cc-skills-golang/golang-structs-interfaces.md Repositorio: https://github.com/samber/cc-skills-golang Autor: samber Licencia: MIT Actualizado: el mes pasado Coste de contexto: 156 tok instalada, 3.4k tok al activarse, 6.8k tok con todos los archivos del bundle Bundle: 2 archivos, 26 KB Permisos que pide: read edit write glob grep bash(go:*) bash(golangci-lint:*) bash(git:*) agent askuserquestion ## Instalación Un skill son archivos markdown: los mismos archivos valen para cualquier agente y lo único que cambia es el directorio de destino, es decir la bandera `--agent`. Añade `-g` para instalarlo en todos los proyectos de la máquina. ```bash # Claude Code npx -y skills add samber/cc-skills-golang --skill golang-structs-interfaces --agent claude-code # Cursor npx -y skills add samber/cc-skills-golang --skill golang-structs-interfaces --agent cursor # Codex npx -y skills add samber/cc-skills-golang --skill golang-structs-interfaces --agent codex # Gemini CLI npx -y skills add samber/cc-skills-golang --skill golang-structs-interfaces --agent gemini # Windsurf npx -y skills add samber/cc-skills-golang --skill golang-structs-interfaces --agent windsurf # Cline npx -y skills add samber/cc-skills-golang --skill golang-structs-interfaces --agent cline ``` ## Qué hace - Guía diseño de tipos Go: interfaces pequeñas, composición, embedding, aserciones y switches de tipo - Recomienda aceptar interfaces y devolver structs concretos desde constructores - Define reglas para struct tags de serialización JSON/YAML/DB y consistencia de receptores puntero vs valor - Sugiere patrón noCopy y compile-time interface checks con `var _ Interface = (*Type)(nil)` ## Cuándo usarla - Al diseñar tipos Go, definir o implementar interfaces - Al embeber structs o interfaces, o escribir aserciones/type switches - Al añadir struct field tags para serialización JSON/YAML/DB - Al elegir entre receptores puntero y valor ## Qué la activa - "Diseña una interfaz para este servicio de notificaciones" - "¿Debería usar receptor puntero o valor en este struct?" - "Ayúdame a añadir tags JSON y DB a este struct" - "Revisa si esta interfaz es demasiado grande" ## Antes de instalar - Pensado para proyectos en Golang con el binario `go` disponible. - makes network requests ## Archivos - SKILL.md — 13 KB - evals/evals.json — 13 KB ## SKILL.md Reproducido tal cual desde samber/cc-skills-golang bajo MIT. Esta sección es el documento original y está en inglés. **Persona:** You are a Go type system designer. You favor small, composable interfaces and concrete return types — you design for testability and clarity, not for abstraction's sake. > **Community default.** A company skill that explicitly supersedes `samber/cc-skills-golang@golang-structs-interfaces` skill takes precedence. # Go Structs & Interfaces ## Interface Design Principles ### Keep Interfaces Small > "The bigger the interface, the weaker the abstraction." — Go Proverbs Interfaces SHOULD have 1-3 methods. Small interfaces are easier to implement, mock, and compose. If you need a larger contract, compose it from small interfaces: → See `samber/cc-skills-golang@golang-naming` skill for interface naming conventions (method + "-er" suffix, canonical names) ```go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } // Composed from small interfaces type ReadWriter interface { Reader Writer } ``` Compose larger interfaces from smaller ones: ```go type ReadWriteCloser interface { io.Reader io.Writer io.Closer } ``` ### Define Interfaces Where They're Consumed Interfaces Belong to Consumers. Interfaces MUST be defined where consumed, not where implemented. This keeps the consumer in control of the contract and avoids importing a package just for its interface. ```go // package notification — defines only what it needs type Sender interface { Send(to, body string) error } type Service struct { sender Sender } ``` The `email` package exports a concrete `Client` struct — it doesn't need to know about `Sender`. ### Accept Interfaces, Return Structs Functions SHOULD accept interface parameters for flexibility and return concrete types for clarity. Callers get full access to the returned type's fields and methods; consumers upstream can still assign the result to an interface variable if needed. ```go // Good — accepts interface, returns concrete func NewService(store UserStore) *Service { ... } // BAD — NEVER return interfaces from constructors func NewService(store UserStore) ServiceInterface { ... } ``` ### Don't Create Interfaces Prematurely > "Don't design with interfaces, discover them." NEVER create interfaces prematurely — wait for 2+ implementations or a testability requirement. Premature interfaces add indirection without value. Start with concrete types; extract an interface when a second consumer or a test mock demands it. ```go // Bad — premature interface with a single implementation type UserRepository interface { FindByID(ctx context.Context, id string) (*User, error) } type userRepository struct { db *sql.DB } // Good — start concrete, extract an interface later when needed type UserRepository struct { db *sql.DB } ``` ## Make the Zero Value Useful Design structs so they work without explicit initialization. A well-designed zero value reduces constructor boilerplate and prevents nil-related bugs: ```go // Good — zero value is ready to use var buf bytes.Buffer buf.WriteString("hello") var mu sync.Mutex mu.Lock() // Bad — zero value is broken, requires constructor type Registry struct { items map[string]Item // nil map, panics on write } // Good — lazy initialization guards the zero value func (r *Registry) Register(name string, item Item) { if r.items == nil { r.items = make(map[string]Item) } r.items[name] = item } ``` ## Avoid `any` / `interface{}` When a Specific Type Will Do Since Go 1.18+, MUST prefer generics over `any` for type-safe operations. Use `any` only at true boundaries where the type is genuinely unknown (e.g., JSON decoding, reflection): ```go // Bad — loses type safety func Contains(slice []any, target any) bool { ... } // Good — generic, type-safe func Contains[T comparable](slice []T, target T) bool { ... } ``` ## Key Standard Library Interfaces | Interface | Package | Method | | ------------- | --------------- | ------------------------------------- | | `Reader` | `io` | `Read(p []byte) (n int, err error)` | | `Writer` | `io` | `Write(p []byte) (n int, err error)` | | `Closer` | `io` | `Close() error` | | `Stringer` | `fmt` | `String() string` | | `error` | builtin | `Error() string` | | `Handler` | `net/http` | `ServeHTTP(ResponseWriter, *Request)` | | `Marshaler` | `encoding/json` | `MarshalJSON() ([]byte, error)` | | `Unmarshaler` | `encoding/json` | `UnmarshalJSON([]byte) error` | Canonical method signatures MUST be honored — if your type has a `String()` method, it must match `fmt.Stringer`. Don't invent `ToString()` or `ReadData()`. ## Compile-Time Interface Check Verify a type implements an interface at compile time with a blank identifier assignment. Place it near the type definition: ```go var _ io.ReadWriter = (*MyBuffer)(nil) ``` This costs nothing at runtime. If `MyBuffer` ever stops satisfying `io.ReadWriter`, the build fails immediately. ## Type Assertions & Type Switches ### Safe Type Assertion Type assertions MUST use the comma-ok form to avoid panics: ```go // Good — safe s, ok := val.(string) if !ok { // handle } // Bad — panics if val is not a string s := val.(string) ``` ### Type Switch Discover the dynamic type of an interface value: ```go switch v := val.(type) { case string: fmt.Println(v) case int: fmt.Println(v * 2) case io.Reader: io.Copy(os.Stdout, v) default: fmt.Printf("unexpected type %T\n", v) } ``` ### Optional Behavior with Type Assertions Check if a value supports additional capabilities without requiring them upfront: ```go type Flusher interface { Flush() error } func writeData(w io.Writer, data []byte) error { if _, err := w.Write(data); err != nil { return err } // Flush only if the writer supports it if f, ok := w.(Flusher); ok { return f.Flush() } return nil } ``` This pattern is used extensively in the standard library (e.g., `http.Flusher`, `io.ReaderFrom`). ## Struct & Interface Embedding ### Struct Embedding Embedding promotes the inner type's methods and fields to the outer type — composition, not inheritance: ```go type Logger struct { *slog.Logger } type Server struct { Logger addr string } // s.Info(...) works — promoted from slog.Logger through Logger s := Server{Logger: Logger{slog.Default()}, addr: ":8080"} s.Info("starting", "addr", s.addr) ``` The receiver of promoted methods is the _inner_ type, not the outer. The outer type can override by defining its own method with the same name. ### When to Embed vs Named Field | Use | When | | --- | --- | | **Embed** | You want to promote the full API of the inner type — the outer type "is a" enhanced version | | **Named field** | You only need the inner type internally — the outer type "has a" dependency | ```go // Embed — Server exposes all http.Handler methods type Server struct { http.Handler } // Named field — Server uses the store but doesn't expose its methods type Server struct { store *DataStore } ``` ## Dependency Injection via Interfaces Accept dependencies as interfaces in constructors. This decouples components and makes testing straightforward: ```go type UserStore interface { FindByID(ctx context.Context, id string) (*User, error) } type UserService struct { store UserStore } func NewUserService(store UserStore) *UserService { return &UserService{store: store} } ``` In tests, pass a mock or stub that satisfies `UserStore` — no real database needed. ## Struct Field Tags Use field tags for serialization control. Exported fields in serialized structs MUST have field tags: ```go type Order struct { ID string `json:"id" db:"id"` UserID string `json:"user_id" db:"user_id"` Total float64 `json:"total" db:"total"` Items []Item `json:"items" db:"-"` CreatedAt time.Time `json:"created_at" db:"created_at"` DeletedAt time.Time `json:"-" db:"deleted_at"` Internal string `json:"-" db:"-"` } ``` | Directive | Meaning | | ----------------------- | ------------------------------------------- | | `json:"name"` | Field name in JSON output | | `json:"name,omitempty"` | Omit field if zero value | | `json:"-"` | Always exclude from JSON | | `json:",string"` | Encode number/bool as JSON string | | `db:"column"` | Database column mapping (sqlx, etc.) | | `yaml:"name"` | YAML field name | | `xml:"name,attr"` | XML attribute | | `validate:"required"` | Struct validation (go-playground/validator) | ## Pointer vs Value Receivers | Use pointer `(s *Server)` | Use value `(s Server)` | | --- | --- | | Method modifies the receiver | Receiver is small and immutable | | Receiver contains `sync.Mutex` or similar | Receiver is a basic type (int, string) | | Receiver is a large struct | Method is a read-only accessor | | Consistency: if any method uses a pointer, all should | Map and function values (already reference types) | Receiver type MUST be consistent across all methods of a type — if one method uses a pointer receiver, all methods should. ## Preventing Struct Copies with `noCopy` Some structs must never be copied after first use (e.g., those containing a mutex, a channel, or internal pointers). Embed a `noCopy` sentinel to make `go vet` catch accidental copies: ```go // noCopy may be added to structs which must not be copied after first use. // See https://pkg.go.dev/sync#noCopy type noCopy struct{} func (*noCopy) Lock() {} func (*noCopy) Unlock() {} type ConnPool struct { noCopy noCopy mu sync.Mutex conns []*Conn } ``` `go vet` reports an error if a `ConnPool` value is copied (passed by value, assigned, etc.). This is the same technique the standard library uses for `sync.WaitGroup`, `sync.Mutex`, `strings.Builder`, and others. Always pass these structs by pointer: ```go // Good func process(pool *ConnPool) { ... } // Bad — go vet will flag this func process(pool ConnPool) { ... } ``` ## Cross-References - → See `samber/cc-skills-golang@golang-naming` skill for interface naming conventions (Reader, Closer, Stringer) - → See `samber/cc-skills-golang@golang-design-patterns` skill for functional options, constructors, and builder patterns - → See `samber/cc-skills-golang@golang-dependency-injection` skill for DI patterns using interfaces - → See `samber/cc-skills-golang@golang-code-style` skill for value vs pointer function parameters (distinct from receivers) - → See `samber/cc-skills-golang@golang-gopls` skill for safe rename and the `implementInterface` code action — renaming a method or receiver that participates in interface satisfaction updates every call site and refuses a rename that would silently break the interface, which grep/sed cannot detect ## Common Mistakes | Mistake | Fix | | --- | --- | | Large interfaces (5+ methods) | Split into focused 1-3 method interfaces, compose if needed | | Defining interfaces in the implementor package | Define where consumed | | Returning interfaces from constructors | Return concrete types | | Bare type assertions without comma-ok | Always use `v, ok := x.(T)` | | Embedding when you only need a few methods | Use a named field and delegate explicitly | | Missing field tags on serialized structs | Tag all exported fields in marshaled types | | Mixing pointer and value receivers on a type | Pick one and be consistent | | Forgetting compile-time interface check | Add `var _ Interface = (*Type)(nil)` | | Using `ToString()` instead of `String()` | Honor canonical method names | | Premature interface with a single implementation | Start concrete, extract interface when needed | | Nil map/slice in zero value struct | Use lazy initialization in methods | | Using `any` for type-safe operations | Use generics (`[T comparable]`) instead | ## Dónde encaja - Categoría: [Herramientas para desarrolladores](https://skillsagentes.com/categorias/herramientas-desarrollo.md) — Skills que cambian cómo tu agente escribe, revisa y despliega código. - Creador: [samber](https://skillsagentes.com/creators/samber.md) — 0 skills en el directorio - [Todas las skills](https://skillsagentes.com/skills.md) - [Ranking de instalaciones](https://skillsagentes.com/ranking.md) ## Otras skills del mismo repositorio - [Golang Lint](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-lint.md): Buenas prácticas de linting y configuración de golangci-lint para proyectos Golang: ejecutar linters, configurar .golangci.yml, suprimir avisos con nolint, interpretar salidas y elegir linters. - [Golang How To](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-how-to.md): Orquestador de skills de Golang, siempre activo en cualquier tarea de código, revisión, debug o setup: carga las skills más relevantes de samber/cc-skills-golang, a menudo varias a la vez. - [Golang Benchmark](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-benchmark.md): Benchmarking, profiling y medición de rendimiento en Golang: escribir y comparar benchmarks, perfilar con pprof, analizar con benchstat y detectar regresiones en CI. - [Golang Testing](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-testing.md): Tests de Golang listos para producción: table-driven, suites y mocks con testify, tests paralelos, fuzzing, fixtures, detección de fugas de goroutines con goleak, snapshot testing, cobertura, tests de integración. - [Golang Performance](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-performance.md): Patrones y metodología de optimización de rendimiento en Golang: si hay cuello de botella X, aplica el patrón Y, una vez que profiling o benchmarks ya lo identificaron. ## Skills relacionadas - [Golang Samber Hot](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-samber-hot.md): Caché en memoria en Golang con samber/hot: algoritmos de expulsión (LRU, LFU, TinyLFU, S3FIFO, ARC, TwoQueue, SIEVE, FIFO), TTL, loaders, sharding y métricas Prometheus. - [Golang Samber Lo](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-samber-lo.md): Helpers funcionales para Go con samber/lo: 500+ funciones genéricas type-safe para slices, maps, canales, strings, math, tuplas y concurrencia. - [Golang Samber Mo](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-samber-mo.md): Tipos monádicos para Golang con samber/mo — Option, Result, Either, Future, IO, Task y State para valores nulos seguros, manejo de errores y composición funcional. - [Golang Samber Slog](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-samber-slog.md): Extensiones de logging estructurado para Go con paquetes samber/slog-****: pipelines multi-handler, sampling, formateo de atributos, middleware HTTP y enrutamiento a backends como Datadog, Sentry o Loki. - [Golang Spf13 Viper](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-spf13-viper.md): Librería de configuración en Go con spf13/viper: precedencia en capas (flag > env > archivo > KV > default), BindPFlag, AutomaticEnv, Unmarshal con mapstructure, Sub, WatchConfig y aislamiento en tests. --- Skills Agentes · [Índice de páginas en markdown](https://skillsagentes.com/sitemap.md) · [Inicio](https://skillsagentes.com/index.md)