I am following a Go tutorial and am stuck as I cant understand a particular method signature:
func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
The docs explain this as follows:
This method's signature reads: "This is a method named save that takes as its receiver p, a pointer to Page . It takes no parameters, and returns a value of type error."
I cant understand what the receiver is. I would read this as it being a parameter but then I would expect a parameter to be in save().
The receiver is just a special case of a parameter. Go provides syntactic sugar to attach methods to types by declaring the first parameter as a receiver.
For instance:
func (p *Page) save() error
reads "attach a method called save that returns an error to the type *Page", as opposed to declaring:
func save(p *Page) error
that would read "declare a function called save that takes one parameter of type *Page and returns an error"
As proof that it's only syntactic sugar you can try out the following code:
p := new(Page)
p.save()
(*Page).save(p)
Both last lines represent exactly the same method call.
Also, read this answer.
The receiver is the object on what you declare your method.
When want to add a method to an object, you use this syntax.
ex: http://play.golang.org/p/5n-N_Ov6Xz
Related
I just started learning Go a few days ago.Today, we were debugging a piece of code for a while when we found something that seem counterintuitive of Go to do.
First we defined an interface and a data structure that implements it.
type Executer interface {
Execute()
}
type whatever struct {
name string
}
func (this *whatever) Execute() {
log.Println(this.name)
}
Now consider that I have a nil pointer to whatever and I try to call the method Execute. In other object-oriented languages I have used so far, this would call a null pointer error at the point of calling the method (i.e. w.Execute()) since the object pointer is null. Interestingly, in Go, the method is invoked, the null pointer error occurs at the Execute method when I try to dereference this.name. Why not at the point of calling the method?
func main() {
var w *whatever
w.Execute()
}
So, what I'm seeking to understand now is how is this possible? Does this mean that Go only does early method binding at compile time and at runtime there is no binding of the method with a specific object?
The receiver is just an "ordinary" argument to the function. Ordinary parameters may be of pointer types. When then are, you are allowed to pass nil as the argument, which is perfectly valid. All you need to keep in mind is not to dereference nil pointer arguments. The same applies to the special receiver parameter too. If it's a pointer, it may be nil, you just must not dereference it.
Spec: Method declarations:
The receiver is specified via an extra parameter section preceding the method name.
... The method is said to be bound to its receiver base type and the method name is visible only within selectors for type T or *T.
Allowing nil receiver values is not just something not forbidden, it has practical uses. For an example, see Test for nil values in nested stucts.
In Java you can call static methods on null objects too. It's true you can't do the same in Go, because Go does not have modifiers like static, public, private etc. In Go there are only exported and non-exported methods (implied by the first latter of their name).
But Go offers something similar too: Method expressions and Method values. If you have a method m with T receiver type, the expression T.m will be a function value whose signature contains the parameters and result types of m "prefixed" with the receiver type.
For example:
type Foo int
func (f Foo) Bar(s string) string { return fmt.Sprint(s, f) }
func main() {
fooBar := Foo.Bar // A function of type: func(Foo, string) string
res := fooBar(1, "Go")
fmt.Println(res)
}
Foo.Bar will be a function with type func (Foo, string) string, and you can call it like any other ordinary function; and you also have to pass the receiver as the first argument. The above app outputs (try it on the Go Playground):
Go1
Going a little "forward", we are not required to store Foo.Bar in a variable, we can directly call Foo.Bar:
fmt.Println(Foo.Bar(1, "Go"))
Which outputs the same (try it on the Go Playground). Now this almost looks like a static method call in Java.
And as the "final" step, when we use the above expression on a value of Foo itself (instead of the type identifier), we arrive at the method values, which saves the receiver and so the type of a method value does not include the receiver, its signature will be that of the method, and we can call it without having to pass a receiver:
var f Foo = Foo(1)
bar := f.Bar
fmt.Println(bar("Go"))
This again will output the same, try it on the Go Playground.
See related questions:
Pass method argument to function
golang function alias on method receiver
I was just messing around and wrote the below piece of code,
package main
import (
"fmt"
)
type Person struct {
name string
}
func (p Person) printName() {
fmt.Println(p.name)
}
type Man struct {
name string
f func()
}
func main() {
p := Person{name: "John"}
m := Man{name: "Adam"}
m.f = p.printName
p.printName()
m.f()
}
The above code results in the following output. This works across packages too.
John
John
So, here are my questions.
Why does this work?
Struct methods require receivers of the same type. How is the function still able to access members of the Person struct?
What happens when m.f = p.printName is executed in the above example?
This question deals mostly with receivers and may extend a bit to embedding.
From the relevant section of the spec:
A method is a function with a receiver. A method declaration binds an
identifier, the method name, to a method, and associates the method
with the receiver's base type.
MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver = Parameters .
The receiver is specified via an extra parameter section preceding the
method name. That parameter section must declare a single non-variadic
parameter, the receiver. Its type must be of the form T or *T
(possibly using parentheses) where T is a type name. The type denoted
by T is called the receiver base type; it must not be a pointer or
interface type and it must be defined in the same package as the
method. The method is said to be bound to the base type and the method
name is visible only within selectors for type T or *T.
A non-blank receiver identifier must be unique in the method
signature. If the receiver's value is not referenced inside the body
of the method, its identifier may be omitted in the declaration. The
same applies in general to parameters of functions and methods.
For a base type, the non-blank names of methods bound to it must be
unique. If the base type is a struct type, the non-blank method and
field names must be distinct.
Given type Point, the declarations
func (p *Point) Length() float64 {
return math.Sqrt(p.x * p.x + p.y *p.y)
}
func (p *Point) Scale(factor float64) {
p.x *= factor
p.y *= factor
}
bind the methods Length and Scale, with receiver type *Point, to the
base type Point.
The type of a method is the type of a function with the receiver as
first argument.
For instance, the method Scale has type
func(p *Point, factor float64)
However, a function declared this way is not a method.
Man has a field named f that is a function that takes no arguments and returns nothing.
As we saw above golang internally treats
func (p Person) printName()
as
func printName(p Person)
and this can be considered as a function of no arguments when it acts on a Person struct (and it does because p is a Person and p.printName acts on p). Therefore it is allowed to be assigned to Man.f
So the moment you assigned the f field on the Man struct to a function that has captured the Person instance with name "John" and reads the name from that, therefore you get effect of the second "John" being printed.
The Man.name field has never come into play on that one.
I suspect that what you would expect as normal behaviour can be achieved with struct embedding of Person into a Man.
here is a playground link to demonstrate that
A method, in Go, is basically a function with a receiver. That receiver is wraped by the compiler, and beyond that, there is nothing more different from a normal function. That means, at anywhere, the method always gets the receiver which it is bound to, no matter how you call it, assign it to another variable or anythiing else.
In your code, f is not a method of type Man. It is merely a field of type func(). You can set to anything that match the signature, and the function will know nothing about Man or its instance. That means, m.f has no knowledge of m and no access to m.name or any other field of m.
And a note, you can call methods like: Person.PrintName(p) where p is of type Person.
function in Golang is also a value, it can be passed as parameter or assigned by other function too, method in Golang is also a function, with one different, it has receiver in it's value, so when use assign a method to a function (which have same signature) it's refer to the receiver and executing code in that method to destination function
I'm working on unit tests on a Go project, and I'm new to Go. So to start I wanted to test something easy. And I started with this function:
func (this *Service) InList(idPerson string, personsId []string) bool {
for _, personsId := range personsId {
if id == idPerson {
return true
}
}
return false
}
Service is a struct defined on top of the class.
This is the test I wrote:
func TestValidatePersonID(t *testing.T) {
personID := "12345"
personIDs := []string{"12345", "123456t", "1234567a"}
ok := *Service.InList(personID, personIDs)
if !ok {
t.Errorf("Id %v not found", personID)
}
}
If i try to Call Service without * I get the error:
invalid method expresion (needs pointer reciever)
If i try to call the function (*Service).inList, it says I'm missing an argument. I'm new to Go if anyone could point to me what I'm doing wrong and how Could I get a pointer receiver of that Service in my test?. I would appreciatte it.
The correct syntax for the method expression is:
ok := (*Service).InList(nil, personID, personIDs)
This snippet adds nil as the receiver argument and uses parentheses to specify the type correctly.
The approached used in the question is not idiomatic. Either call a method on a value
s := Service{}
ok := s.InList(personID, personIDs)
or convert the method to a function.
You have to call a method on an instance of its receiver type. So, for a method defined on *Service, you must call it on an instance of *Service:
var foo *Service
foo = &Service{}
foo.InList(personID, personIDs)
However, in your case, there's no reason for this to be a method; it doesn't seem to have anything at all to do with its receiver (it never references it), so it could just be a regular function. Also note that it's unidiomatic to name the receiver this in Go.
I also highly recommend at least taking the Go tour, which covers writing methods in detail, with interactive examples.
If you do not reference the receiver object, then you should not have one, keep your code as simple as possible.
There are three ways of writing a method or function, with each its own purpose.
without receiver, when no receiver is referenced in the function ( we call this a function )
a value receiver, the receiver is referenced, but not changed in the method ( we call this a method )
a pointer receiver, something in the receiver will be changed in the method
In most of the function declarations I've seen in Go, the format is Name, Arguments, Output. Such as the following:
func add(a, b int) int {
return a + b
}
But in this example, the pointer is given in the beginning of the declaration, instead of in the argument section right after the name. My question is: what is the cause is this format? Are pointers written differently when they're arguments?
func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
in go there are no classes in the way you usually see them .. instead we use the syntax in question do declare methods on types, the argument in front is called areceiver and if we use a pointer receiver we get reference type semantics otherwise you get value type semantics. check out the go tour it is awesome.
Looking at the Go documentation shown below, I'm having trouble understanding the distinction between receivers and parameters:
func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
This method's signature reads:
This is a method named save that takes as its receiver p, a pointer to
Page . It takes no parameters, and returns a value of type error.
The receiver is like this in C#: in x.f(a, b, c) the receiver is x and the arguments are a, b and c. When the function is executed the parameters refer to copies of the arguments. The important difference between the receiver and parameters is that when the receiver is an interface type at the call site, the function to be called is determined dynamically rather than statically.