Structs, Embedding y Promoción de Campos
Las structs agrupan campos con nombre en un único tipo.
Busca en todas las páginas de la documentación
Las structs agrupan campos con nombre en un único tipo.
El embedding (incrustación) te permite componer comportamiento y campos de otros tipos sin herencia, promoviendo miembros exportados al tipo externo.
Una literal de struct establece campos por nombre o posición (si no es ambigua).
Los campos incrustados deben no tener nombre y el nombre de su tipo se convierte en un nombre de campo para la promoción.
Los campos y métodos promovidos son invocables en el valor externo como si estuvieran declarados allí, a menos que sean ocultados (shadowed).
Tarjeta de referencia rápida - lista para copiar y pegar.
package main
import "fmt"
type Logger struct{}
func (Logger) Log(msg string) { fmt.Println(msg) }
type Server struct {
Logger // Campo incrustado anónimo
Addr string
}
func main() {
s := Server{Addr: ":8080"}
s.Log("listening") // método promovido
fmt.Println(s.Addr)
}Cuándo usar esto:
User, Order, Config).io.Reader, sync.Mutex).package main
import "fmt"
type Person struct {
Name string
Age int
}
type Employee struct {
Person // Campo incrustado anónimo
ID int
Dept string
}
func (p Person) Greet() string {
return "hello, " + p.Name
}
func describe(e Employee) {
// Los campos y métodos de Person se promueven a Employee
fmt.Println(e.Name, e.ID, e.Greet())
}
func main() {
e := Employee{
Person: Person{Name: "Ada", Age: 36}, // Inicialización anidada
ID: 1001,
Dept: "research",
}
describe(e)
// El campo externo explícito prevalece sobre el promovido si los nombres colisionan
type Shadow struct {
Person // Campo incrustado
Name string // Campo con el mismo nombre que en Person
}
sh := Shadow{Person: Person{Name: "Grace"}, Name: "Override"}
// sh.Name se refiere al campo Name de Shadow, no al de Person
fmt.Println(sh.Name, sh.Person.Name)
}Lo que esto demuestra:
Person incrustado a Employee.e.Name accede a Person.Name incrustado.| Forma | Ejemplo | Cuándo |
|---|---|---|
| Campos con nombre | Person{Name: "Ada"} | Por defecto - claro y sin orden |
| Posicional | Person{"Ada", 36} | Solo para structs pequeñas y estables |
| Literal de puntero | &Person{Name: "Ada"} | APIs que esperan *Person |
| Parcial | Person{Name: "Ada"} | Los otros campos quedan en su valor cero |
| Situación | Resultado |
|---|---|
Un único T incrustado | outer.Field si T.Field está exportado |
Método en T | Invocable como outer.Method() |
| Colisión de nombres en el tipo externo | El campo externo oculta el promovido; usa outer.T.Field |
| Múltiples incrustaciones en conflicto | Acceso ambiguo - error del compilador sin calificador |
// Incrustar una interfaz para reenviar comportamiento
type ReadCloser struct {
io.Reader // Campo incrustado anónimo
io.Closer // Campo incrustado anónimo
}
// Incrustar mutex - el valor cero es utilizable
type SafeMap struct {
sync.Mutex // Campo incrustado anónimo
m map[string]int
}*T incrustado con valor nulo causa pánico al llamar a un método promovido. Solución: asegurar la inicialización o proteger las llamadas.encoding/json por defecto. Solución: nombrar los campos o ajustar las etiquetas cuando la anidación importa.==; comparar campos manualmente.Name se aplica. Solución: evitar nombres de campo duplicados entre tipos externos e incrustados.| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
Campo con nombre person Person | Propiedad clara, sin promoción | Quieres métodos promovidos |
| Embedding | Composición real de tipo "es un" (Server tiene logging) | Solo agrupación simple de datos |
| Interfaces | Abstracción de comportamiento | Solo necesitas campos de datos |
| Paquetes separados | Ocultar implementación incrustada | Ayudantes locales estrechos |
Los campos y métodos exportados de un tipo incrustado aparecen en el tipo externo sin un calificador.
e.Name funciona cuando Person está incrustado y tiene Name.
Sí, si los nombres promovidos no entran en conflicto.
Las promociones ambiguas requieren selectores explícitos como e.Person.Name.
El campo incrustado es un campo real almacenado dentro de la struct externa por valor (o puntero si es *T).
No es un enlace de referencia.
Si T tiene func (t *T) M(), la struct externa O con T incrustado obtiene M cuando O o *O es direccionable según las reglas del receptor.
Solo puedes incrustar tipos nombrados, no int desnudos.
Define type UserID int e incrusta UserID si es necesario.
Los campos de struct anónimos se serializan en línea (los campos se promueven al nivel superior) a menos que la etiqueta json:",inline" cambie el comportamiento para structs anidadas.
Los campos con nombre se anidan normalmente.
Tipo marcador de tamaño cero utilizado para claves de conjuntos map[K]struct{} y para señalar canales.
No asigna bytes adicionales para el valor.
Las etiquetas son metadatos de cadena leídos mediante reflexión (reflect.StructTag).
Paquetes como encoding/json y ORMs las interpretan en tiempo de ejecución.
Solo si todos los campos son comparables.
Los slices, mapas y funciones en el interior rompen la comparabilidad.
La asignación copia los tipos de valor campo por campo.
Para campos de puntero, la copia duplica los punteros; realiza una copia profunda manualmente si es necesario.
Usa *T al mutar, para evitar el costo de copia de structs grandes, o para representar presencia opcional.
Los registros pequeños e inmutables pueden permanecer como valores.
Incrustar unexportedType solo promueve a tipos en el mismo paquete.
Los paquetes externos no pueden acceder a los miembros no exportados promovidos.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (Green Tea GC por defecto, go fix modernizers - verificar parche en la compilación), chi (última - verificar en la compilación), gin (última - verificar en la compilación), echo (última - verificar en la compilación), google.golang.org/grpc (última - verificar en la compilación), sigs.k8s.io/controller-runtime (última - verificar en la compilación), kubebuilder (última - verificar en la compilación), tinygo (última - verificar objetivos de placa en la compilación), wazero (última - verificar en la compilación), y golangci-lint (última - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 19 jul 2026