A struct is a typed collection of fields, short for structure. It groups related data into a single record.
Struct Basics
Declare a struct with type and struct. Each field inside the braces gets a name and a type.
type Product struct {
Name string
Price float64
}
p := Product{Name: "USB-C Cable", Price: 12.99}A comment after a field, or on its own line directly above with no blank line between, documents what that field holds. go doc and godoc read these comments and show them next to the field, the same way they document a function or type.
type Customer struct {
Name string // full name shown on the invoice
Email string // used only for order confirmations, never displayed
}Use dot notation, like p.Price, to read or write a field. An uninitialized struct still gets default zero values for every field.
p.Price = 9.99 // dot notation
var zero Product
fmt.Printf("%+v\n", zero) // {Name: Price:0}Struct Embedding
Drop a struct inside another without giving it a field name. Its fields become directly available on the outer struct.
type Timestamps struct {
CreatedAt string
}
type Order struct {
Timestamps
ID string
}
o := Order{Timestamps: Timestamps{CreatedAt: "2026-08-01"}, ID: "ORD-1042"}
fmt.Println(o.CreatedAt) // 2026-08-01, reached directlyIf both structs have a field with the same name, the outer one wins. The inner field is still there, reachable through its type name.
type Order struct {
Timestamps
ID string
CreatedAt string // this field wins
}
o := Order{ID: "ORD-1042", CreatedAt: "2026-08-02"}
o.Timestamps.CreatedAt = "2026-08-01"
fmt.Println(o.CreatedAt) // 2026-08-02
fmt.Println(o.Timestamps.CreatedAt) // 2026-08-01Anonymous Structs
An anonymous struct has no name. It's built inline for one-off jobs, like a table of test cases.
tests := []struct {
input int
expected bool
}{
{2, true},
{7, false},
{10, true},
}
for _, tt := range tests {
got := tt.input%2 == 0
fmt.Println(tt.input, got == tt.expected)
}Struct Tags
A struct tag is a short label after a field's type. Tools like JSON encoders read it to rename a key or skip an empty field.
type Order struct {
ID string `json:"id"`
Total float64 `json:"total"`
Note string `json:"note,omitempty"`
}
b, _ := json.Marshal(Order{ID: "ORD-1042", Total: 58.50})
fmt.Println(string(b)) // {"id":"ORD-1042","total":58.5}Comparing & Copying
Use == to compare two structs. This only works if every field can be compared. For time.Time values, use Equal instead.
type Point struct{ X, Y int }
a := Point{X: 3, Y: 4}
b := Point{X: 3, Y: 4}
fmt.Println(a == b) // true
t1 := time.Now()
t2 := t1.Round(0)
fmt.Println(t1 == t2) // false
fmt.Println(t1.Equal(t2)) // trueCopying a struct duplicates its simple fields, like numbers and strings. A slice or map field still points to the same underlying data as the original.
type Cart struct {
ID string
Items []string
}
a := Cart{ID: "c1", Items: []string{"pen", "paper"}}
b := a
b.ID = "c2"
b.Items[0] = "pencil"
fmt.Println(a.ID, a.Items) // c1 [pencil paper]
fmt.Println(b.ID, b.Items) // c2 [pencil paper]