It’s a named collection of fields to store related data.

package main
 
import "fmt"
 
type Vertex struct {
	X int
	Y int
}
 
func main() {
	v := Vertex{1, 2}
	v.X = 4
	fmt.Println(v.X)  // output "4"
}

Struct can be defined inside or outside of a function. When defined inside a function, its scope is that function.

Init

Different ways to initialize the struct:

package main
 
import "fmt"
 
type Vertex struct {
	X, Y int
}
 
var (
	v1 = Vertex{1, 2}  // has type Vertex
	v2 = Vertex{X: 1}  // Y:0 is implicit
	v3 = Vertex{}      // X:0 and Y:0
	p  = &Vertex{1, 2} // has type *Vertex
)
 
func main() {
	fmt.Println(v1, v2, v3, p) // output "{1 2} {1 0} {0 0} &{1 2}"
}

If you don’t provide a value for the field inside a struct, it will be initialized with a zero value for its type.


When using a pointer to a struct, Go treats p.X treats the same as (*p).X (with explicit dereference).

Embedding

package main
 
type A struct {
	year int
}
 
func (a A) Greet() { fmt.Println("Hello GolangUK", a.year) }
 
type B struct {
	A
}
 
func (b B) Greet() { fmt.Println("Welcome to GolangUK", b.year) }
 
func main() {
	var a A
	a.year = 2016
	var b B
	b.year = 2016
	a.Greet() // Hello GolangUK 2016
	b.Greet() // Welcome to GolangUK 2016
}

We have a type A, with a field year and a method Greet. We have a second type, B which embeds an A, thus callers see Bβ€˜s methods overlaid on Aβ€˜s because A is embedded, as a field, within B, and B can provide its own Greet method, obscuring that of A.

But embedding isn’t just for methods, it also provides access to an embedded type’s fields. As you see, because both A and B are defined in the same package, B can access Aβ€˜s private year field as if it were declared inside B.

But:

package main
 
type Cat struct {
	Name string
}
 
func (c Cat) Legs() int { return 4 }
 
func (c Cat) PrintLegs() {
	fmt.Printf("I have %d legs\n", c.Legs())
}
 
type OctoCat struct {
	Cat
}
 
func (o OctoCat) Legs() int { return 5 }
 
func main() {
	var octo OctoCat
	fmt.Println(octo.Legs()) // 5
	octo.PrintLegs()         // I have 4 legs
}

In this example we have a Cat type, which can count its number of legs with its Legs method. We embed this Cat type into a new type, an OctoCat, and declare that Octocats have five legs. However, although OctoCat defines its own Legs method, which returns 5, when the PrintLegs method is invoked, it returns 4.

This is because PrintLegs is defined on the Cat type. It takes a Cat as its receiver, and so it dispatches to Catβ€˜s Legs method. Cat has no knowledge of the type it has been embedded into, so its method set cannot be altered by embedding.

Thus, we can say that Go’s types, while being open for extension, are closed for modification. https://dave.cheney.net/2016/08/20/solid-go-design

Anonymous struct

// 1
var person struct {
    name string
    age  int
    pet  string
}
 
person.name = "bob"
person.age = 50
person.pet = "dog"
 
// 2
pet := struct {
    name string
    kind string
}{
    name: "Fido",
    kind: "dog",
}

They are useful for:

Comparison

Structs are comparable when it contains only comparable fields.

Structs of different types can’t be compared, even if they have the same set of fields. But if one of the struct is anonymous, you can compare them.

Conversion

Instances of a different structs type with the same set of fields can be converted.

Struct tags

You can provide annotations for each field on the struct:

type User struct {
	Name          string    `json:"name"`
	Password      string    `json:"password"`
	PreferredFish []string  `json:"preferredFish"`
	CreatedAt     time.Time `json:"createdAt"`
}

Well-known struct tags.