HowtoGo
Home / Data Structures / Structs

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 directly

If 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-01

Anonymous 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)) // true

Copying 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]