Menu

Golang Struct: Define, Initialize, Compare and Tag Structs

Structs group named fields into one type. Learn how to define and initialize them, zero values, pointers to structs, anonymous structs, comparison, struct tags, and the NewX constructor convention.

This page includes runnable editors - edit, run, and see output instantly.

Defining and creating a struct

A struct type is a list of named fields. You declare it with type Name struct { ... } and create values with a composite literal.

Output:

Ana 31
{Name:Ana Email:ana@example.com Age:32 Admin:false}
{Name: Email: Age:0 Admin:false}
main.User{Name:"Bob", Email:"", Age:0, Admin:false}

%+v prints field names and %#v prints Go syntax. Both are the quickest way to inspect a struct while debugging.

Fields of the same type can share a line: X, Y float64. Capitalized field names are exported (visible to other packages and to encoding/json); lowercase ones are not.

Literal forms

User{Name: "Ana", Age: 31}        // named fields: order-free, omitted fields are zero
User{"Ana", "a@x.com", 31, false} // positional: every field, in order
&User{Name: "Ana"}                // pointer to a new struct
new(User)                         // pointer to a zeroed struct

Prefer named fields. Positional literals break when someone adds or reorders a field, and go vet warns about them for struct types from other packages (for example net/url.Error struct literal uses unkeyed fields). Positional form is fine for tiny local types like Point{1, 2}.

No default values: zero values and constructors

Go has no field defaults and no constructors in the language. Every field starts at the zero value of its type: 0, "", false, nil. Two idioms fill the gap.

Make the zero value useful. sync.Mutex, bytes.Buffer and strings.Builder all work without initialization. When you can design a type that way, do.

Write a NewX function when a field needs a non-zero default or something must be allocated, like a map:

NewServer returns a pointer because the server has methods that modify it and will be shared. Returning the struct by value is also common for small immutable types (time.Date returns a time.Time).

Pointers to structs

Go dereferences struct pointers automatically for field access. With p := &User{}, you write p.Name, not (*p).Name.

Output:

{11 12}
{11 12} {0 12}
{11 0}

Structs are values. Assignment and function arguments copy them; a pointer shares one. Field-by-field copies are shallow: a slice or map field in the copy still points at the same data. See pointers for when to use which.

Anonymous structs

A struct type can be written inline without a name. This is useful for one-off groupings, table-driven tests and decoding JSON you only read once.

The []struct{ in string; want int } table is the standard shape of a Go test.

Comparing structs

Structs are comparable with == when every field is comparable. Equality means all fields are equal. Comparable structs can also be map keys.

For a struct with slice or map fields, write an Equal method or compare in tests with reflect.DeepEqual.

Struct tags

A tag is a string literal after a field's type. The language ignores it; libraries read it with reflection. The most common reader is encoding/json:

This prints {"id":7,"name":"Mug"}: the tag renames fields, omitempty drops the zero Price, and the unexported field is invisible to the encoder. Tags follow the key:"value" convention, with several keys separated by spaces: `json:"name" db:"user_name"`. A malformed tag compiles fine and is silently ignored, which go vet catches. The json page covers the options.

Empty structs and field order

struct{} has no fields and takes zero bytes. It is used as a set value (map[string]struct{}) and as a signal on channels (chan struct{}), where only the event matters.

Field order affects memory layout: the compiler pads fields to their alignment. A struct of bool, int64, bool takes 24 bytes on 64-bit systems, while int64, bool, bool takes 16. This matters only for types stored in large numbers.

Common mistakes

  • Modifying a copy. for _, u := range users { u.Age++ } changes a copy each time. Use the index: users[i].Age++.
  • Forgetting to initialize a map field. The struct's zero value holds a nil map; writing to it panics. Allocate in a constructor.
  • Copying a struct that holds a sync.Mutex. The copy has its own lock state. Pass such structs by pointer; go vet reports the copy.
  • Positional literals for other packages' types. They break when a field is added.

Frequently Asked Questions

How do you initialize a struct in Go?

With a composite literal. Named fields are the usual form: u := User{Name: "Ana", Age: 31}, and any field you omit gets its zero value. User{} gives all zero values, &User{...} gives a pointer, and new(User) gives a pointer to a zeroed struct.

Does Go support default values for struct fields?

No. Every field starts at the zero value of its type. The idiom is a constructor function, func NewServer() *Server { return &Server{Port: 8080} }, or designing the type so its zero value is already useful, as sync.Mutex and bytes.Buffer are.

How do you compare two structs in Go?

With ==, if every field is comparable. Two struct values are equal when all corresponding fields are equal. A struct containing a slice, map or function field cannot be compared with == (compile error); compare the fields yourself or use reflect.DeepEqual in tests.

What are struct tags in Go?

String literals after a field's type, like `json:"name,omitempty"`. The compiler ignores them; packages such as encoding/json, database drivers and validators read them with reflection to control how fields are encoded, named or checked.

How do I print a struct with field names in Go?

Use fmt.Printf("%+v\n", s), which prints {Name:Ana Age:31}. %v prints only the values, and %#v prints Go syntax including the type name: main.User{Name:"Ana", Age:31}.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED