Skip to content

Structs

Structs are a way to arrange data in specific formats. They are similar to classes in other languages but without inheritance.

Defining Structs

// Declaring a struct
type Person struct {
    Name string
    Age  int
}

// Struct with multiple types
type Employee struct {
    ID        int
    FirstName string
    LastName  string
    Email     string
    Salary    float64
    IsActive  bool
}

Creating Struct Instances

// Method 1: Positional initialization
person := Person{"John", 34}
person.Name // "John"
person.Age // 34

// Method 2: Named fields
person2 := Person{
    Name: "Alice",
    Age:  20,
}
person2.Name // "Alice"
person2.Age // 20

// Method 3: Zero value initialization
person3 := Person{}
person3.Name // ""
person3.Age // 0

// Method 4: Using new (returns pointer)
person4 := new(Person)
person4.Name // ""
person4.Age // 0

Accessing and Modifying Fields

type Person struct {
    Name string
    Age  int
}

person := Person{"John", 30}

// Access fields
fmt.Println(person.Name) // John

// Modify fields
person.Age = 31
fmt.Println(person.Age) // 31

Nested Structs

type Address struct {
    Street  string
    City    string
    ZipCode string
}

type Person struct {
    Name    string
    Age     int
    Address Address
}

person := Person{
    Name: "John",
    Age:  30,
    Address: Address{
        Street:  "123 Main St",
        City:    "New York",
        ZipCode: "10001",
    },
}

fmt.Println(person.Address.City) // New York

Embedded Structs (Composition)

type Address struct {
    Street string
    City   string
}

type Person struct {
    Name string
    Age  int
    Address // Embedded struct
}

person := Person{
    Name: "John",
    Age:  30,
    Address: Address{
        Street: "123 Main St",
        City:   "New York",
    },
}

// Can access embedded fields directly
fmt.Println(person.Street) // 123 Main St
fmt.Println(person.City)   // New York

// Or through the embedded struct
fmt.Println(person.Address.Street) // 123 Main St

Anonymous Structs

// Useful for one-time use
person := struct {
    Name string
    Age  int
}{
    Name: "John",
    Age:  30,
}

fmt.Println(person.Name) // John

Struct Tags

// Tags provide metadata about fields
type User struct {
    ID        int    `json:"id" db:"user_id"`
    FirstName string `json:"first_name" db:"first_name"`
    LastName  string `json:"last_name" db:"last_name"`
    Email     string `json:"email" validate:"required,email"`
}

// Used by packages like encoding/json
import "encoding/json"

user := User{
    ID:        1,
    FirstName: "John",
    LastName:  "Doe",
    Email:     "john@example.com",
}

jsonData, _ := json.Marshal(user)
fmt.Println(string(jsonData))
// {"id":1,"first_name":"John","last_name":"Doe","email":"john@example.com"}

Comparing Structs

type Point struct {
    X int
    Y int
}

p1 := Point{1, 2}
p2 := Point{1, 2}
p3 := Point{2, 3}

fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // false

// Note: Structs with slices, maps, or functions cannot be compared

Copying Structs

type Person struct {
    Name string
    Age  int
}

person1 := Person{"John", 30}

// Shallow copy
person2 := person1
person2.Name = "Jane"

fmt.Println(person1.Name) // John (unchanged)
fmt.Println(person2.Name) // Jane

Struct Methods

Methods are covered in detail in the Methods and Interfaces section, but here's a quick example:

type Rectangle struct {
    Width  float64
    Height float64
}

// Method with value receiver
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// Method with pointer receiver
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

rect := Rectangle{Width: 10, Height: 5}
fmt.Println(rect.Area()) // 50

rect.Scale(2)
fmt.Println(rect.Width)  // 20
fmt.Println(rect.Height) // 10

Constructor Pattern

type Person struct {
    name string // unexported
    age  int    // unexported
}

// Constructor function
func NewPerson(name string, age int) *Person {
    return &Person{
        name: name,
        age:  age,
    }
}

// Getter methods
func (p *Person) Name() string {
    return p.name
}

func (p *Person) Age() int {
    return p.age
}

// Setter methods
func (p *Person) SetAge(age int) {
    if age > 0 {
        p.age = age
    }
}

person := NewPerson("John", 30)
fmt.Println(person.Name()) // John