Start learning free

LESSON 184 OF 338 · OBJECT-ORIENTED PROGRAMMING

Class Methods in Go: Receiver Methods

Lesson Read the full explanation

TL ; DR:

  • A method is a function with a receiver declared for a struct. It can read and write the struct's fields.


  • Define methods with a receiver so they can be called on struct values:

    type Dog struct {
        name string
    }
    
    func NewDog(name string) Dog {
        return Dog{name: name}
    }
    
    func (d Dog) bark() {
        fmt.Printf("Woof! I am %s\n", d.name)
    }
    

  • Call it on a value with the dot operator: dog.bark()



Full lesson:

So far you've added data to a struct with fields and a constructor function. The other half of OOP is behavior — actions a value can perform. That's what methods are for.


Your task

Add a bark() method that prints "Woof! I am " followed by the dog's name.

Woof! I am Rex
STEP 1 OF 4
    Ctrl + Enter to check

    GO · OBJECT-ORIENTED PROGRAMMING

    Class Methods in Go, explained

    Behaviour belongs to the type

    Fields give a type data, and methods give it behaviour: actions a value can perform. A Go method is a function with one extra parameter, the receiver, written in parentheses between func and the method name. The receiver says which type the method belongs to and gives the method a name for the value it was called on.

    type Dog struct {
        name string
    }
    
    func NewDog(name string) Dog {
        return Dog{name: name}
    }
    
    func (d Dog) bark() {
        fmt.Printf("Woof! I am %s\n", d.name)
    }
    

    Inside bark, the name d is the dog the method was called on, so d.name reads that dog's field. You call the method with the dot operator: dog.bark().

    Methods live outside the struct

    In Go a method is not declared inside the struct braces. The struct lists only data, and each method is a separate top-level declaration, which can sit anywhere in the same package. A type can have as many methods as it needs, and each one can take further parameters and return values like any function:

    func (d Dog) describe(times int) string {
        return strings.Repeat(d.name+" ", times)
    }
    

    The receiver name

    The receiver is an ordinary parameter, so you pick its name. This lesson uses this, which is familiar from other languages. Go code in the wild usually uses a short name taken from the type, such as d for Dog, and keeps it the same across all methods of that type. The compiler accepts both, since this has no special meaning in Go. Use whichever name the lesson asks for here.

    Value receivers and pointer receivers

    The receiver in this lesson is a Dog, a value receiver, so the method works on a copy of the dog. That is fine for reading fields. A method that must change the value needs a pointer receiver, written (d *Dog). Running this shows the difference:

    func (d Dog) birthdayValue()    { d.age++ }
    func (d *Dog) birthdayPointer() { d.age++ }
    
    func main() {
        d := NewDog("Rex", 4)
        d.birthdayValue()
        fmt.Println(d.age) // 4: only a copy changed
        d.birthdayPointer()
        fmt.Println(d.age) // 5
    }
    

    The first call increased the age of a copy, which was thrown away. The second changed d itself. Go takes the address of d for you in the second call because d is a variable you can address. As a rule, use a pointer receiver when the method modifies the value or when the struct is large, and use a value receiver otherwise.

    The lesson's exercise

    The starter code gives Dog and NewDog. The task is a bark method that prints Woof! I am followed by the name, and a mainFunction that uses it.

    1. Declare bark below NewDog, with a receiver of type Dog named this, so the method can read the fields.
    2. Print the message with fmt.Printf. The %s verb inserts the name, and the format string ends in \n because Printf does not add a newline.
    3. Declare mainFunction, the place where the program runs your code.
    4. Create rex with NewDog("Rex") and call rex.bark().
    type Dog struct {
        name string
    }
    
    func NewDog(name string) Dog {
        return Dog{name: name}
    }
    
    func (this Dog) bark() {
        fmt.Printf("Woof! I am %s\n", this.name)
    }
    
    
    
    func mainFunction() {
        rex := NewDog("Rex")
        rex.bark()
    }
    

    It prints:

    Woof! I am Rex
    

    Common mistakes

    • Calling the method as a plain function. bark(rex) does not compile, because bark belongs to Dog. Write rex.bark().
    • Forgetting the receiver. Without (this Dog), func bark() is an ordinary function that has no access to any dog.
    • Expecting a value receiver to modify the dog. It changes a copy. Use a pointer receiver for that.
    • Leaving \n out of a Printf format. Printf prints exactly the format you give it, so the next output would continue on the same line.
    • Declaring two methods with the same name on one type. A type cannot have two methods called bark, even with different parameters, because Go has no method overloading.

    Key takeaways

    • A method is a function with a receiver: func (r Type) name(...).
    • It is declared outside the struct and called with the dot operator.
    • The receiver name is your choice; this is allowed but short type-based names are the Go habit.
    • Value receivers get a copy, pointer receivers can modify the original.
    • Go has no method overloading.
    Keep going with GoAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free