Methods

Back

Loading concept...

Methods in Go: Teaching Your Data New Tricks! 🎪

Imagine you have a toy robot. It can walk, talk, and dance. But wait—who taught the robot these moves? You did! You gave it instructions (methods) that tell it what it can do.

In Go, methods are like teaching your data structures new tricks. A struct is just a box of data—but with methods, that box becomes alive.


🎯 The Big Picture

Think of it this way:

  • Struct = A toy robot (just sitting there, doing nothing)
  • Method = Teaching the robot to wave, jump, or say “Hello!”

Without methods, your robot just sits. With methods, it becomes useful and fun!


1. Method Declaration: The Basic Spell 🪄

A method is a function that belongs to a type. It’s like saying: “Hey, this trick belongs to THIS robot!”

The Magic Formula

func (r Robot) Wave() {
    fmt.Println("Hello!")
}

Let’s break this down like LEGO blocks:

Part What It Means
func “I’m creating a function!”
(r Robot) “This function belongs to Robot”
Wave() “The trick is called Wave”

Simple Example

type Dog struct {
    Name string
}

func (d Dog) Bark() {
    fmt.Println(d.Name + " says: Woof!")
}

// Using it:
myDog := Dog{Name: "Buddy"}
myDog.Bark()  // Buddy says: Woof!

The receiver (d Dog) is like a name tag. It tells Go: “When this method runs, d is the dog we’re talking about.”

graph TD A["Dog Struct"] --> B["Has Name field"] A --> C["Has Bark method"] C --> D["Uses Name to bark"]

2. Value Receivers: Taking a Photo 📸

A value receiver is like taking a photo of your toy. You can look at the photo, but you can’t change the real toy by drawing on the photo!

How It Works

type Counter struct {
    Value int
}

func (c Counter) Show() {
    fmt.Println("Count:", c.Value)
}

func (c Counter) TryToAdd() {
    c.Value = c.Value + 1
    // This only changes the COPY!
}

What Happens?

myCounter := Counter{Value: 5}
myCounter.TryToAdd()
myCounter.Show()  // Still prints 5! 😱

Why? Because TryToAdd received a copy of myCounter. It changed the copy, not the original!

graph TD A["Original Counter: 5"] -->|Copy Made| B["Method Gets Copy: 5"] B -->|Method Adds 1| C["Copy Becomes: 6"] A -->|Stays Same| D["Original Still: 5"]

When to Use Value Receivers

  • ✅ When you just want to read data
  • ✅ When your struct is small (few fields)
  • ✅ When you don’t need to change anything

3. Pointer Receivers: The Remote Control 🎮

A pointer receiver is like having a remote control that actually controls your toy. Press a button, and the real toy moves!

The Secret Symbol: *

func (c *Counter) Add() {
    c.Value = c.Value + 1
    // This changes the REAL counter!
}

The * is the magic! It means: “Don’t give me a copy. Give me the address of the real thing!”

Now It Works!

myCounter := Counter{Value: 5}
myCounter.Add()
myCounter.Show()  // Prints 6! 🎉

Visual Comparison

Receiver Type Symbol Effect
Value (c Counter) Works on a copy
Pointer (c *Counter) Works on the real thing
graph TD A["Original Counter: 5"] -->|Pointer Used| B["Method Changes Original"] B --> C["Original Becomes: 6"]

When to Use Pointer Receivers

  • ✅ When you need to change the data
  • ✅ When your struct is big (copying is expensive)
  • ✅ When you want consistency (if one method uses pointer, all should)

4. Methods on Non-Struct Types: Any Type Can Learn! 🌟

Here’s a surprise: You can add methods to ANY type, not just structs!

But there’s one rule: The type must be defined in YOUR package.

Creating Your Own Type

type MyNumber int

func (n MyNumber) Double() MyNumber {
    return n * 2
}

Using It

num := MyNumber(5)
result := num.Double()
fmt.Println(result)  // 10

Another Fun Example

type Message string

func (m Message) Shout() string {
    return strings.ToUpper(string(m)) + "!"
}

msg := Message("hello")
fmt.Println(msg.Shout())  // HELLO!

What You CAN’T Do

// ❌ WRONG! Can't add methods to built-in types
func (i int) Double() int {
    return i * 2
}

Go will say: “You can’t define methods on types from other packages!”


5. Method Sets: The Rulebook 📖

A method set is a list of all methods a type can use. Think of it as a robot’s menu of tricks.

The Two Different Menus

What You Have What Methods You Get
Counter (value) Only value receiver methods
*Counter (pointer) BOTH value AND pointer methods

Why This Matters

type Robot struct {
    Name string
}

func (r Robot) Walk() {
    fmt.Println(r.Name + " walks")
}

func (r *Robot) Repair() {
    fmt.Println(r.Name + " is repaired")
}

The Good News

Go is smart and helpful! When you call methods, Go converts for you:

robot := Robot{Name: "R2D2"}
robot.Walk()    // Works! (value method)
robot.Repair()  // Also works! Go converts to pointer

ptr := &robot
ptr.Walk()      // Works! Go converts to value
ptr.Repair()    // Works! (pointer method)

Where It Gets Tricky: Interfaces

When using interfaces, the method set rules are strict:

type Walker interface {
    Walk()
}

type Repairer interface {
    Repair()
}

var w Walker = robot     // ✅ OK
var r Repairer = robot   // ❌ Error!
var r2 Repairer = &robot // ✅ OK
graph TD A["Value Type"] -->|Has| B["Value Methods Only"] C["Pointer Type"] -->|Has| D["Value Methods"] C -->|Also Has| E["Pointer Methods"]

6. Promoted Methods: Inheritance Without the Drama 🎭

Go doesn’t have “classes” or “inheritance” like other languages. Instead, it has something simpler: embedding.

When you put one struct inside another (without a name), all its methods get promoted!

The Setup

type Engine struct {
    Power int
}

func (e Engine) Start() {
    fmt.Println("Vroom! Power:", e.Power)
}

type Car struct {
    Engine  // Embedded! No name, just the type
    Brand string
}

The Magic

myCar := Car{
    Engine: Engine{Power: 200},
    Brand:  "GoMobile",
}

// Car can call Engine's method directly!
myCar.Start()  // Vroom! Power: 200

You didn’t write Start() for Car. But because Engine is embedded, Car inherits the method automatically!

Multiple Embeds? No Problem!

type Radio struct{}

func (r Radio) PlayMusic() {
    fmt.Println("Playing music!")
}

type LuxuryCar struct {
    Engine
    Radio
    Brand string
}

luxe := LuxuryCar{
    Engine: Engine{Power: 300},
    Brand:  "GoLux",
}

luxe.Start()      // From Engine
luxe.PlayMusic()  // From Radio

What If Methods Collide?

If two embedded types have the same method name, you must be specific:

type A struct{}
func (a A) Hello() { fmt.Println("A says hi") }

type B struct{}
func (b B) Hello() { fmt.Println("B says hi") }

type C struct {
    A
    B
}

c := C{}
// c.Hello()    // ❌ Error! Ambiguous
c.A.Hello()     // ✅ A says hi
c.B.Hello()     // ✅ B says hi
graph TD A["Engine"] -->|Has| B["Start Method"] C["Car"] -->|Embeds| A C -->|Gets| B D["Radio"] -->|Has| E["PlayMusic Method"] C -->|Can Also Embed| D C -->|Gets| E

🎯 Quick Summary

Concept What It Means Example
Method Declaration Function attached to a type func (d Dog) Bark()
Value Receiver Works on a copy func (c Counter) Show()
Pointer Receiver Works on the original func (c *Counter) Add()
Non-Struct Methods Methods on custom types type MyInt int
Method Sets Which methods a type can use Pointers get ALL methods
Promoted Methods Embedded types share methods Car gets Engine.Start()

🚀 You Did It!

Now you understand how to give your Go types superpowers!

Methods turn boring data into active, useful objects. Value receivers keep things safe. Pointer receivers let you make changes. And promoted methods let you build complex types from simple pieces.

Go out and teach your types some tricks! 🎪

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.