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 vetreports 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}.