← Go EnglishChapter 08 of 13

Structs and Methods

## Learning Objectives - Define and use structs - Create and initialize struct instances - Work with struct fields - Define methods with receivers - Understand value vs pointer receivers - Embed structs for composition ## Structs ### Definition ```go type Person struct { Name string Age int City string } ``` ### Declaration ```go var p Person // Zero-initialized p := Person{} // Also zero-initialized p := Person{Name: "Alice", Age: 30} // With field names p := Person{"Alice", 30, "NYC"} // Positional (brittle) ``` ### Field Access ```go p := Person{Name: "Alice", Age: 30} fmt.Println(p.Name) // Alice fmt.Println(p.Age) // 30 p.Age = 31 // Modify field fmt.Println(p.Age) // 31 ``` ## Creating Instances ### Zero Value ```go var p Person // All fields zero-valued fmt.Println(p) // { 0 } ``` ### Composite Literal ```go p := Person{ Name: "Alice", Age: 30, } ``` ### new Operator ```go p := new(Person) // Returns *Person (pointer) p.Name = "Bob" // Arrow syntax not needed fmt.Println(p) // &{Bob 0} ``` ### Pointer to Literal ```go p := &Person{Name: "Carol", Age: 25} fmt.Println(p) // &{Carol 25} ``` ## Struct with Pointers ```go type Node struct { Value int Next *Node } n1 := &Node{Value: 1} n2 := &Node{Value: 2} n1.Next = n2 fmt.Println(n1.Next.Value) // 2 ``` ## Methods ### Value Receiver ```go type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func main() { r := Rectangle{Width: 10, Height: 5} fmt.Println(r.Area()) // 50 } ``` ### Pointer Receiver ```go type Rectangle struct { Width, Height float64 } func (r *Rectangle) Scale(factor float64) { r.Width *= factor r.Height *= factor } func main() { r := Rectangle{Width: 10, Height: 5} r.Scale(2) fmt.Println(r.Width, r.Height) // 20 10 } ``` ### When to Use Pointers | Situation | Recommendation | |-----------|----------------| | Modify the receiver | Use `*T` | | Mutate a large object | Use `*T` | | Ensure consistency | Use `*T` | | Read-only method | Use `T` or `*T` | ## Named Types ### Type Definition ```go type Celsius float64 type Fahrenheit float64 func (c Celsius) ToFahrenheit() Fahrenheit { return Fahrenheit(c*9/5 + 32) } func main() { var c Celsius = 100 fmt.Printf("%.2f C = %.2f F\n", c, c.ToFahrenheit()) } ``` ## Embedded Structs (Composition) ### Basic Embedding ```go type Address struct { Street string City string State string } type Person struct { Name string Address // Embedded (no field name) } func main() { p := Person{ Name: "Alice", Address: Address{ Street: "123 Main St", City: "NYC", State: "NY", }, } fmt.Println(p.Street) // Direct access (promoted) fmt.Println(p.Address.Street) // Also works } ``` ### Shadowing ```go type A struct { x int } type B struct { A x int // Shadows A.x } func main() { b := B{A: A{x: 1}, x: 2} fmt.Println(b.x) // 2 (B's x) fmt.Println(b.A.x) // 1 (A's x) } ``` ## Struct Tags ### Struct Tag Definition ```go type Person struct { Name string `json:"name"` Age int `json:"age"` } ``` ### Common Tags ```go type Person struct { Name string `json:"name" xml:"name"` Age int `json:"age,omitempty"` } ``` ### Accessing Tags ```go import "reflect" t := reflect.TypeOf(Person{}) field, _ := t.FieldByName("Name") fmt.Println(field.Tag.Get("json")) // name ``` ## Comparing Structs ### Comparable Structs are comparable if all fields are comparable. ```go type Point struct { X, Y int } p1 := Point{1, 2} p2 := Point{1, 2} if p1 == p2 { fmt.Println("Equal") } ``` ### With Non-comparable Fields ```go type Person struct { Name string Slice []int // Not comparable } p1 := Person{Name: "Alice"} p2 := Person{Name: "Alice"} // p1 == p2 // COMPILE ERROR ``` ## Function vs Method ### Function ```go func area(r Rectangle) float64 { return r.Width * r.Height } ``` ### Method ```go func (r Rectangle) area() float64 { return r.Width * r.Height } ``` ## Memory Layout ### Padding ```go type Foo struct { A int8 // 1 byte _ [7]byte // padding B int64 // 8 bytes } type Bar struct { B int64 // 8 bytes A int8 // 1 byte _ [7]byte // padding } ``` ### Order Matters Put larger fields first to minimize padding: ```go type Efficient struct { B int64 // 8 bytes A int8 // 1 byte C int32 // 4 bytes } ``` ## Common Patterns ### Constructor Pattern ```go type Stack struct { items []int } func NewStack() *Stack { return &Stack{items: make([]int, 0)} } func (s *Stack) Push(item int) { s.items = append(s.items, item) } ``` ### Option Pattern ```go type Server struct { Port int Timeout int } type Option func(*Server) func WithPort(port int) Option { return func(s *Server) { s.Port = port } } func NewServer(opts ...Option) *Server { s := &Server{Port: 8080, Timeout: 30} for _, opt := range opts { opt(s) } return s } func main() { s := NewServer(WithPort(9090)) } ``` ## Summary - Structs group related fields together - Use composite literals to initialize: `Person{Name: "Alice"}` - `new(Type)` returns a pointer to zero-initialized struct - Methods have receivers - use pointer receiver to modify - Embedded structs provide composition (no inheritance) - Struct tags annotate fields for encoding/decoding - Struct field order affects memory layout - Use constructor functions for complex initialization

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →