Structs and Data Modeling
Overview
Structs are Go’s primary mechanism for creating custom composite types. They group related fields together.
Defining Structs
type Person struct {
Name string
Age int
Email string
Active bool
}Creating Instances
// Zero value
var p Person // All fields get zero values
// Literal (named fields - preferred)
p := Person{
Name: "Alice",
Age: 30,
Email: "alice@example.com",
}
// Literal (positional - fragile)
p := Person{"Alice", 30, "alice@example.com", true}
// Using new
p := new(Person) // *Person with zero values
// Expression-based new (Go 1.26+)
p := new(Person{Name: "Alice"}) // *Person initialized with valuesEmbedded fields (composition)
Employee
|-- Person
| |-- Name
| |-- Age
|-- Address
| |-- City
|-- Title
Accessing Fields
p.Name = "Bob"
fmt.Println(p.Age)
// Pointer access (automatic dereference)
ptr := &p
ptr.Name = "Carol" // Same as (*ptr).NameAnonymous Structs
point := struct {
X, Y int
}{10, 20}
// Useful for one-off data
data := struct {
ID int
Value string
}{1, "test"}Embedding (Composition)
type Address struct {
Street string
City string
}
type Employee struct {
Person // Embedded
Address // Embedded
Title string
}
e := Employee{
Person: Person{Name: "Alice", Age: 30},
Address: Address{City: "NYC"},
Title: "Engineer",
}
// Promoted fields
e.Name // Same as e.Person.Name
e.City // Same as e.Address.CityComparison
Structs are comparable if all fields are comparable:
p1 := Person{Name: "Alice"}
p2 := Person{Name: "Alice"}
p1 == p2 // trueConstructors
Go uses factory functions:
func NewPerson(name string, age int) *Person {
return &Person{
Name: name,
Age: age,
Active: true, // Default
}
}
func NewPersonWithDefaults() *Person {
return &Person{
Active: true,
}
}Summary
| Pattern | Usage |
|---|---|
| Define | type Name struct { fields } |
| Create | Name{Field: value} |
| Embed | Include type name without field name |
| Tags | Field Type \key:“value”`| | Constructor |func NewType() *Type` |
More examples
Example: literal and field access
Save as main.go and go run . (with go mod init example if needed).
package main
import "fmt"
type User struct {
Name string
Age int
}
func main() {
u := User{Name: "ada", Age: 36}
fmt.Printf("%+v\n", u)
u.Age++
fmt.Println("age:", u.Age)
}Expected:
{Name:ada Age:36}
age: 37
Example: embedding promotes fields
Save as main.go and go run . (with go mod init example if needed).
package main
import "fmt"
type Address struct {
City string
}
type Person struct {
Name string
Address
}
func main() {
p := Person{Name: "lin", Address: Address{City: "Berlin"}}
fmt.Println(p.Name, "in", p.City) // promoted field
}Expected:
lin in Berlin
Runnable example
Save as main.go. From an empty directory:
go mod init example
go run .package main
import (
"encoding/json"
"fmt"
"reflect"
)
type Address struct {
Street string
City string
}
type Person struct {
Name string
Age int
}
type Employee struct {
Person // embedded
Address // embedded
Title string
}
type User struct {
ID int `json:"id"`
Name string `json:"name,omitempty"`
Password string `json:"-"`
}
func NewEmployee(name, title, city string) *Employee {
return &Employee{
Person: Person{Name: name, Age: 0},
Address: Address{City: city},
Title: title,
}
}
func main() {
var zero Person
fmt.Printf("zero person: %+v\n", zero)
e := NewEmployee("Ada", "Engineer", "London")
e.Age = 36 // promoted field
fmt.Printf("employee: name=%s title=%s city=%s age=%d\n", e.Name, e.Title, e.City, e.Age)
// Pointer field access auto-dereferences
p := &Person{Name: "Grace", Age: 85}
p.Age++
fmt.Printf("pointer person: %+v\n", p)
// Anonymous struct for one-off data
point := struct{ X, Y int }{10, 20}
fmt.Printf("point=(%d,%d)\n", point.X, point.Y)
// Comparable when all fields are comparable
a := Person{Name: "Ada", Age: 36}
b := Person{Name: "Ada", Age: 36}
fmt.Println("a==b?", a == b)
// Tags for JSON + reflection
u := User{ID: 7, Name: "Ada", Password: "secret"}
raw, err := json.Marshal(u)
if err != nil {
fmt.Println("json error:", err)
return
}
fmt.Println("json:", string(raw))
t := reflect.TypeOf(User{})
if f, ok := t.FieldByName("Name"); ok {
fmt.Println("Name tag:", f.Tag.Get("json"))
}
if f, ok := t.FieldByName("Password"); ok {
fmt.Println("Password tag:", f.Tag.Get("json"))
}
}Expected output (illustrative):
zero person: {Name: Age:0}
employee: name=Ada title=Engineer city=London age=36
pointer person: &{Name:Grace Age:86}
point=(10,20)
a==b? true
json: {"id":7,"name":"Ada"}
Name tag: name,omitempty
Password tag: -
What to notice: - Embedding promotes fields (e.Name, e.City) without inheritance. - Factory NewEmployee sets defaults; zero Person is still valid. - JSON tags control names, omitempty, and secrets (json:"-"). - Struct equality works when every field is comparable.
Try next: Set Name to "" and remarshal—observe omitempty dropping the field.