People searching for golang array of structs, golang struct array, go array of structs, or golang list of structs usually want a collection of the same struct type. In Go that is either a fixed-length array [n]Person or, much more often, a slice []Person whose length can change. This guide shows golang initialize array of structs with literals, make for preallocated slices (golang make array style), iteration with range, and field updates—including when a slice of pointers []*Student is clearer. For the struct type itself, see structs in Go; for growing collections, see append to slice; for passing collections to functions, see pass array or slice to a function; for pointers, see Go pointers.
Tested with Go 1.24 on Linux.
Fixed array [n]Struct vs slice []Struct
[3]Student: length 3 is part of the type; copying the value copies all three elements.[]Student: header with pointer, length, and capacity;appendcan grow it (see append).
For most “golang array of objects” style lists, use a slice.
golang initialize array of structs (composite literals)
Slice literal (common “golang create array of structs” pattern):
package main
import "fmt"
type Student struct {
Name string
Grade int
}
func main() {
students := []Student{
{Name: "Alice", Grade: 90},
{Name: "Bob", Grade: 85},
}
fmt.Println(students[0].Name, len(students))
}You should see Alice 2.
Fixed-size array literal:
package main
import "fmt"
type Student struct {
Name string
Grade int
}
func main() {
team := [2]Student{
{Name: "Ada", Grade: 99},
{Name: "Lin", Grade: 94},
}
fmt.Println(team[1].Name)
}You should see Lin.
golang make array of structs (make + assign)
make applies to slices (and maps/channels), not to array types [n]T:
package main
import "fmt"
type Student struct {
Name string
Grade int
}
func main() {
s := make([]Student, 2)
s[0] = Student{Name: "Mia", Grade: 88}
s[1].Name = "Noor"
s[1].Grade = 91
fmt.Println(s)
}You should see two structs with the grades you set. Use make([]Student, 0, cap) when you plan to append many rows.
Iterate with range (go array of structs)
package main
import "fmt"
type Student struct {
Name string
Grade int
}
func main() {
students := []Student{
{"Alice", 90},
{"Bob", 85},
}
for i, st := range students {
fmt.Println(i, st.Name, st.Grade)
}
}You should see two lines with indices 0 and 1. For loop variants, see for loops in Go.
Updating fields: value slice vs []*Student
With []Student, st := range gives a copy; mutating st does not write back unless you assign by index:
package main
import "fmt"
type Student struct {
Name string
Grade int
}
func main() {
students := []Student{{"Ada", 90}}
students[0].Grade = 95
fmt.Println(students[0].Grade)
}You should see 95.
When helpers take *Student and mutate, a pointer slice avoids index noise:
package main
import "fmt"
type Student struct {
Name string
Grade int
}
func bump(s *Student) { s.Grade++ }
func main() {
students := []*Student{
{Name: "Ada", Grade: 90},
}
bump(students[0])
fmt.Println(students[0].Grade)
}You should see 91.
Summary
Golang array of structs can mean a true array [n]T or, more often, a slice []T acting as a golang list of structs. Use composite literals for golang initialize array of structs, make([]T, len) when you need allocation first (golang make array), and for range to walk rows. Update fields with index assignment on value slices, or use []*Struct when functions take pointers. Prefer slices unless the length is a fixed part of your model.
References
- Go spec — Composite literals
- Effective Go — Arrays
- Effective Go — Slices
- Structs in Go
- Append to slice

