In Go, a receiver parameter is a special parameter in a method declaration that allows a type to define methods that can be called on its instances.

func (r ReceiverType) MethodName(arguments) { 
	// method implementation 
}

The ReceiverType is the type on which the method is defined, and it can be either a value or a pointer type. The MethodName is the name of the method, and it can be any valid identifier.

When a method is called on a value of the ReceiverType, Go automatically passes a copy of the value as the receiver parameter to the method. If the receiver parameter is a pointer type, Go passes a copy of the pointer, which points to the original value.

Receiver parameters allow types to define behavior that is specific to their instances, and they provide a way to encapsulate the implementation of methods within the type definition.

Example 1: A receiver parameter on a value type

type MyInt int

func (m MyInt) IsPositive() bool {
    return m > 0
}

func main() {
    var i MyInt = 5
    fmt.Println(i.IsPositive()) // Output: true
}

In this example, we define a type MyInt which is a value type that is an alias for the int type. We then define a method IsPositive on the MyInt type, which returns a boolean indicating whether the value is positive or not. The method takes a receiver parameter m of type MyInt, which is the instance of the type that the method is called on. In the main function, we create an instance of MyInt and call the IsPositive method on it.

Example 2: A receiver parameter on a pointer type

type Person struct {
    Name string
    Age  int
}

func (p *Person) IsAdult() bool {
    return p.Age >= 18
}

func main() {
    person := &Person{Name: "Alice", Age: 25}
    fmt.Println(person.IsAdult()) // Output: true
}

In this example, we define a Person struct with two fields Name and Age. We then define a method IsAdult on the Person type, which returns a boolean indicating whether the person is an adult or not. The method takes a receiver parameter p of type *Person, which is a pointer to the instance of the type that the method is called on. In the main function, we create a pointer to a Person and call the IsAdult method on it.

How it is differnt than normal method parameter

let’s understand the difference using an example

type Rectangle struct {
    width, height float64
}

// A method with a receiver parameter
func (r Rectangle) Area() float64 {
    return r.width * r.height
}

// A method with a regular method parameter
func Perimeter(width, height float64) float64 {
    return 2 * (width + height)
}

func main() {
    rect := Rectangle{width: 10, height: 5}

    // Calling a method with a receiver parameter
    area := rect.Area()

    // Calling a method with regular method parameters
    perimeter := Perimeter(rect.width, rect.height)
}

we define a Rectangle struct and two methods: Area and Perimeter.

The Area method is defined with a receiver parameter r of type Rectangle, and it calculates the area of the rectangle.

The Perimeter method is defined with two regular method parameters width and height, and it calculates the perimeter of a rectangle given its width and height.

In the main function, we create an instance of Rectangle and call both methods on it.

When we call the Area method, the Rectangle instance is automatically passed as the receiver parameter r to the method.

When we call the Perimeter method, we pass the width and height values of the Rectangle instance as regular method parameters.