Declaring Structs

A struct groups named fields under one type name; build values with or without field names, or let every field start at zero.

Syntaxtype Book struct { Title string; Pages int }

A struct is one value made of several named parts. A book has a title, a page count and a stock level. Keeping those three in three separate variables means passing three things everywhere and keeping them in step by hand. A struct keeps them together as one value with one type name.

Declaring a struct type

You declare the type once, then make as many values of it as you like. Each named part is a field. A field has a name and a type. A field name that starts with a capital letter can be used from other packages; a lowercase one can only be used inside the package that declared it.

Three ways to build a value

A composite literal builds a value by listing its contents in braces. With field names, as in Book{Title: "Dune", Pages: 412}, the order does not matter and you may leave fields out. Anything left out gets its zero value. Without field names, as in Book{"Dune", 412, 2}, you must give every field, in declaration order. Declaring a variable with no literal at all, var b Book, gives a struct whose every field holds its zero value: 0 for numbers, "" for strings, false for booleans.

Reading and writing fields

A dot reaches a field. b.Title reads it and b.Title = "Go" writes it.

What trips people up

The form without field names is tied to field order. Add a field, or swap two of the same type, and old literals either stop compiling or quietly put values in the wrong fields. Use field names everywhere except very small types such as a two-number point.

Example

Example · go
package main

import "fmt"

// Book is a struct type: one name for a group of related values.
type Book struct {
	Title string
	Pages int
	Stock int
}

func main() {
	// Field names given, so the order does not matter.
	a := Book{Title: "Go in Practice", Pages: 320, Stock: 4}

	// No field names: every field must be listed, in declaration order.
	b := Book{"Dune", 412, 2}

	// No literal at all: each field holds its type's zero value.
	var c Book

	fmt.Println(a.Title, a.Pages, a.Stock)
	fmt.Println(b.Title, b.Pages, b.Stock)

	// %q puts quotes round a string, so an empty one is visible.
	fmt.Printf("%q %d %d\n", c.Title, c.Pages, c.Stock)
}

// Output:
// Go in Practice 320 4
// Dune 412 2
// "" 0 0

When to use it

  • Model a database row or an API payload as one named type instead of loose variables.
  • Return several related values from a function as a single value.
  • Build a list of records, such as a cart or a result page, as a slice of structs.
  • Start a value with every field at its zero value and fill fields in as they are known.

More examples

A struct inside a struct

A field's type can be another struct. Reaching a nested field takes one dot per level, and a nested field can be assigned directly. This is the shape of most real records: an order that holds a shipping address, a user that holds a set of preferences.

Example · go
package main

import "fmt"

type Address struct {
	City string
	Code string
}

type Customer struct {
	Name string
	Home Address
}

func main() {
	c := Customer{
		Name: "Ada",
		Home: Address{City: "Leeds", Code: "LS1"},
	}

	// One dot per level: c, then Home, then City.
	fmt.Println(c.Name, c.Home.City)

	c.Home.Code = "LS2"
	fmt.Println(c.Home.Code)
}

// Output:
// Ada Leeds
// LS2

A slice of structs, with the type name left out

A slice of structs is how a list of records is held in memory: a shopping cart, a page of search results, the rows read back from a database. The shorthand that drops the repeated element type is what you will see in real code, so recognise both forms as the same thing.

Example · go
package main

import "fmt"

type Item struct {
	Name string
	Cost int
}

func main() {
	// Longhand: each element names its type.
	long := []Item{Item{Name: "pen", Cost: 2}, Item{Name: "pad", Cost: 5}}

	// Shorthand: inside a []Item literal the element type is already known,
	// so Item can be dropped. These two slices are identical.
	short := []Item{
		{Name: "pen", Cost: 2},
		{Name: "pad", Cost: 5},
	}

	total := 0
	for _, it := range short {
		total += it.Cost
	}
	fmt.Println(len(long), len(short), total)
}

// Output:
// 2 2 7

An anonymous struct

When a grouping is needed in exactly one place, the type can be written where the value is built instead of being declared at package level. Reach for it for a one-off bundle of values, and declare a named type as soon as a second piece of code needs to mention it.

Example · go
package main

import "fmt"

func main() {
	// A struct type written in place, with no name of its own.
	page := struct {
		Title string
		Views int
	}{Title: "Home", Views: 12}

	fmt.Println(page.Title, page.Views)
}

// Output:
// Home 12

Discussion

  • Be the first to comment on this lesson.