Skip to main content
Go’s structs are typed collections of fields. They’re useful for grouping data together to form records, similar to classes in other languages but without inheritance.

Defining and using structs

package main

import "fmt"

// This person struct type has name and age fields.
type person struct {
    name string
    age  int
}

// newPerson constructs a new person struct with the given name.
func newPerson(name string) *person {
    // Go is garbage collected; you can safely return a pointer
    // to a local variable - it will only be cleaned up when
    // there are no active references to it.
    p := person{name: name}
    p.age = 42
    return &p
}

func main() {
    // This syntax creates a new struct.
    fmt.Println(person{"Bob", 20})

    // You can name the fields when initializing a struct.
    fmt.Println(person{name: "Alice", age: 30})

    // Omitted fields will be zero-valued.
    fmt.Println(person{name: "Fred"})

    // An & prefix yields a pointer to the struct.
    fmt.Println(&person{name: "Ann", age: 40})

    // It's idiomatic to encapsulate new struct creation in constructor functions
    fmt.Println(newPerson("Jon"))

    // Access struct fields with a dot.
    s := person{name: "Sean", age: 50}
    fmt.Println(s.name)

    // You can also use dots with struct pointers -
    // the pointers are automatically dereferenced.
    sp := &s
    fmt.Println(sp.age)

    // Structs are mutable.
    sp.age = 51
    fmt.Println(sp.age)
}

Struct initialization

p := person{"Bob", 20}
Fields must be in order. Less clear but more concise.

Anonymous structs

// If a struct type is only used for a single value, you don't
// have to give it a name. This is commonly used for table-driven tests.
dog := struct {
    name   string
    isGood bool
}{
    "Rex",
    true,
}
fmt.Println(dog)
Anonymous structs are particularly useful in tests, especially for table-driven tests where you define test cases inline.

Constructor functions

It’s idiomatic in Go to create constructor functions for your structs:
func newPerson(name string) *person {
    p := person{name: name}
    p.age = 42  // Set default values
    return &p
}
Go is garbage collected. Returning a pointer to a local variable is safe — the memory won’t be freed as long as something references it.

Struct features

Mutable

Struct fields can be changed after creation

Pointer auto-dereference

structPtr.field works just like (*structPtr).field

Zero values

Uninitialized fields get the zero value for their type

Exported fields

Capitalize field names to export them from packages

Common patterns

func NewPerson(name string, age int) (*person, error) {
    if age < 0 {
        return nil, fmt.Errorf("age cannot be negative")
    }
    return &person{name: name, age: age}, nil
}
type Employee struct {
    person  // Embedded struct
    salary int
}

e := Employee{
    person: person{name: "Alice", age: 30},
    salary: 50000,
}
type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

Best practices

  • Use named fields when initializing structs for clarity
  • Create constructor functions for complex initialization
  • Use pointers to structs when you need to modify them or avoid copying
  • Start field names with uppercase to export them from your package
  • Use struct tags for serialization (JSON, XML, etc.)

Methods

Add methods to struct types

Interfaces

Use structs with interfaces

Struct embedding

Compose types with embedding

Build docs developers (and LLMs) love