Skip to Content
Go Realm v1 is released 🎉
TopicsReceiver Function

Receiver Functions in Go

What Are Receiver Functions?

Receiver functions allow you to define methods on types. They’re Go’s way of attaching functionality to structs (or any user-defined type).

type User struct { Name string Age int } // Receiver function func (u User) Greet() string { return fmt.Sprintf("Hello, %s!", u.Name) }

Basic Syntax

func (receiverName ReceiverType) MethodName(parameters) returnTypes { // Method body }

Value vs Pointer Receivers

FeatureValue Receiver (u User)Pointer Receiver (u *User)
Modifies original❌ No✅ Yes
Memory UsageCopies entire valueUses reference (4/8 bytes)
Best ForRead-only operationsMutating operations
Nil SafetyAlways safeMay cause panics
// Value receiver (read-only) func (u User) GetAge() int { return u.Age } // Pointer receiver (can modify) func (u *User) Birthday() { u.Age++ }

FAQ: Common Interview Questions

1. When to use pointer receivers?

A: Use when:

  • You need to modify the receiver
  • Working with large structs (avoid copying)
  • Implementing interface methods that require mutation

2. Can any type have methods?

A: Yes, but with restrictions:

type MyInt int func (m MyInt) Double() int { return int(m * 2) }

Exception: Cannot define methods on:

  • Built-in types directly (must alias)
  • Pointers directly
  • Interfaces

3. What’s the convention for receiver names?

A: Typically 1-2 letters matching type:

  • u for User
  • p for Product
  • c for Client

4. Can interfaces have receiver methods?

A: No, interfaces define method signatures but can’t implement them.

5. Can built-in types have methods?

A: Only via type aliasing:

type Celsius float64 func (c Celsius) ToFahrenheit() float64 { return float64(c)*9/5 + 32 }

6. What’s the zero-value receiver behavior?

A: Methods can be called on nil receivers if properly handled:

func (a *Account) Balance() float64 { if a == nil { return 0.0 } return a.balance }

7. How does Go handle method calls on values vs pointers?

A: Go automatically converts:

  • value.Method() works for both value and pointer receivers
  • pointer.Method() works for both receiver types

Practical Patterns

Method Chaining

type Calculator struct { Value float64 } func (c *Calculator) Add(x float64) *Calculator { c.Value += x return c } func (c *Calculator) Multiply(x float64) *Calculator { c.Value *= x return c } // Usage: result := new(Calculator).Add(5).Multiply(2).Value // 10

Interface Implementation

type Speaker interface { Speak() string } type Dog struct{} func (d Dog) Speak() string { return "Woof!" } // Dog implicitly implements Speaker

Nil Receiver Handling

func (u *User) SafeGreet() string { if u == nil { return "Hello, guest!" } return fmt.Sprintf("Hello, %s", u.Name) } // Usage: var emptyUser *User fmt.Println(emptyUser.SafeGreet()) // "Hello, guest!"

Common Pitfalls & Solutions

  1. Inconsistent Receiver Types

    // Avoid mixing receiver types for the same struct type Product struct{} func (p Product) GetPrice() float64 {...} // Value func (p *Product) SetPrice() {...} // Pointer
  2. Accidental Value Copies

    func (u User) UpdateName() { u.Name = "Changed" // Doesn't modify original! }
  3. Nil Pointer Panics

    var u *User u.Birthday() // Panic!
  4. Consistency Matters

    • Choose one receiver type (value or pointer) per struct
    • Exception: When specific methods need mutation capability
  5. Receiver Naming

    • Keep short (1-2 chars) and type-related:
      • u for User
      • c for Client
      • db for Database
  6. Size Considerations

    // Use pointer receivers for large structs type BigData struct { data [1_000_000]int } func (b *BigData) Process() {...} // Better for memory

Memory Aid

  • Value receiver = View-only
  • Pointer receiver = Permission to modify
  • (t T) = “Take a copy”
  • (t *T) = “Take the original”