In Go, if I define a function with pointer as the receiver, shouldn't it allow call to the function from a pointer only? Why is it ok to call this function from the value itself and have the same effect.
For example, in following program: m1.reset() & m2.reset() have the same effect. Even though m1 is a value and m2 is a pointer.
I'm a bit confused as there are two ways of doing the same thing and am not sure which one to follow. Though most of the code follows the convention of calling the function using pointer field. Am I missing something?
package main
import "fmt"
type MyStruct struct {
X int
}
func (m *MyStruct) reset() {
m.X = 0
}
func main() {
m1 := MyStruct{1}
m2 := &MyStruct{1}
fmt.Println(m1.X)
fmt.Println(m2.X)
m1.reset()
m2.reset()
fmt.Println(m1.X)
fmt.Println(m2.X)
}
Go pointers store the memory addresses of variables. And just like regular variables, we can also pass pointers to functions. Before you learn how to use pointers with functions, make sure to know about In Go, we can pass pointers as arguments to a function. For example,
But Go is the modern language and so it has many new features baked in it, and Function Receiver is one of it. Go is not an Object-Oriented Programming Language. Function Receiver sets a method on variables that we create. This seems weird at first, but when you get the example, it will be clear as crystal.
Go programming language allows you to pass a pointer to a function. To do so, simply declare the function parameter as a pointer type. In the following example, we pass two pointers to a function and change the value inside the function which reflects back in the calling function −
Pointer receiver passes the address of a type to the function. The function stack has a reference to the original object. So any modifications on the passed object will modify the original object. This shows that the method with value receivers modifies a copy of an object, And the original object remains unchanged.
@jnml offers the perfect doc spec explanation, but I wanted to add a code example based on yours. I think your focus should be less about "Why are there two ways to do the same thing" and more about when to use one vs the other. A method which has a pointer as the receiver has the ability to modify the values of that receiver, while a method which has a value as the receiver cannot. This is because the methods receive a copy of the receiver. When you get a copy of a pointer, you can still modify its value. When you receive a copy of the value, changes you make in that method only change the copy, and never the original:
package main
import "fmt"
type MyStruct struct {
X int
}
func (m *MyStruct) resetPtr() {
m.X = 0
}
func (m MyStruct) resetValue() {
m.X = 0
}
func main() {
m1 := MyStruct{1}
m2 := &MyStruct{1}
fmt.Println("Original Values:", m1.X, m2.X)
m1.resetPtr()
m2.resetPtr()
fmt.Println("After resetPtr():", m1.X, m2.X)
m1 = MyStruct{1}
m2 = &MyStruct{1}
m1.resetValue()
m2.resetValue()
fmt.Println("After resetValue():", m1.X, m2.X)
}
Output
Original Values: 1 1
After resetPtr(): 0 0
After resetValue(): 1 1
You can see that the way you access these variables isn't really the issue. Its more about what you can do with them inside of the method, and, how they are passed as arguments to other functions or methods (being copied).
Specs says:
The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T).
The next piece of necessary info about method calls says:
A method call
x.m()
is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m,x.m()
is shorthand for(&x).m()
.
Put the two above things together and you get the behavior you see.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With