Compose your own types from named fields.
Embedding and Struct Tags
Embedding — composition, not inheritance
Put a type without a field name inside a struct and you "embed" it. The outer struct gets the embedded type's fields and methods, accessible directly.
Embedding is not inheritance. There's no polymorphism between Dog and
Animal — a function expecting Animal won't accept Dog. What you get is
field/method promotion: it's syntactic convenience. For polymorphism you use
interfaces (next chapter).
Struct tags — metadata for encoders
After each field you can attach a back-tick tag. The standard library uses these for JSON, XML, database mapping, validation, and so on.
type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"` // omit empty fields
Pass string `json:"-"` // never serialize
}
The tag syntax is just a back-tick string. Different packages look at different keys; ones you'll see often:
| Tag key | Used by |
| ------------ | -------------------------------- |
| json:"..." | encoding/json |
| xml:"..." | encoding/xml |
| db:"..." | sqlx, pgx, etc. |
| validate: | go-playground/validator |
| yaml:"..." | gopkg.in/yaml.v3 |
Embedding interfaces in structs
You can also embed an interface in a struct. The struct then "is-a" that interface — you only need to provide implementations for methods you want to override. Useful for testing (embed a real type, override one method) or for default behavior:
type LoggingDB struct {
*sql.DB // embed: get all methods for free
log func(string)
}
func (l *LoggingDB) Query(q string, args ...any) (*sql.Rows, error) {
l.log(q)
return l.DB.Query(q, args...) // delegate
}