-1

I am looking into the chapter Writing Web Application of the Golang docs, and found this syntax of a function:

func (p *Page) save() error {
    filename := p.Title + ".txt"
    return os.WriteFile(filename, p.Body, 0600)
}

And from the docs... 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.

  1. What is the difference between declaring the function like this func save(p *Page) error{}? What is the difference between a receiver and a function parameter?
  2. Why is it returning a value of type error, when I see it is returning a function from the os package?

Thank you in advance, may be a dumb question but I'm kinda new to Golang,

Dylan Reimerink
  • 5,874
  • 2
  • 15
  • 21

1 Answers1

1
  1. What is the difference between declaring the function like this func save(p *Page) error{}? What is the difference between a receiver and a function parameter?

These are functionally identical, this is what Go does underwater when compiling. But there are a few semantic differences:

  • The largest difference is that you would call func (p *Page) save() error like somePage.save() and func save(p *Page) error as save(somePage).
  • Documentation generated by go.dev will list it as a function in the package, not as method for Page (assuming it was an exported function).
  • When declaring a func like func save(p *Page) error will not make Page implement an interface like interface{ save() } where as the method will.
  • Go will convert between pointers and values for you when calling a method, but for functions you must always pass the correct param type yourself. For example:
type Page struct {
    Field string
}

func (p *Page) PtrString() string {
    return p.Field
}

func (p Page) String() string {
    return p.Field
}

func PageString(page Page) string {
    return page.Field
}

func PagePtrString(page *Page) string {
    return page.Field
}

func main() {
    p := Page{Field: "Hello world"}

    // Both work
    fmt.Println(p.String())
    fmt.Println(p.PtrString()) // This passes a pointer implicitly

    // This works
    fmt.Println(PageString(p))

    // This doesn't compile
    fmt.Println(PagePtrString(p)) // cannot use p (type Page) as type *Page in argument to PagePtrString

    // But this does, you have to specify you want to pass a pointer
    fmt.Println(PagePtrString(&p))
}

Here is a link with more details about the difference between pointer and value receivers.

  1. Why is it returning a value of type error, when I see it is returning a function from the os package?

It doesn't return the function itself, it calls it and returns the return value of os.WriteFile, which is error

Dylan Reimerink
  • 5,874
  • 2
  • 15
  • 21
  • Okay, now I understand... But I can see that the docs are declaring `Page` by reference like `&Page{Title: "foo", Body: "bar"}`. What would be the difference declaring like `&Page{}` and `Page{}`? Also, by declaring a func like `func (p *Page) save) error` means that if I change the `.Title` of the `p` object inside the function, it will mutate the object itself right? –  Jan 19 '22 at 23:07
  • 1
    The type of `&Page{}` is `*Page`, you declare it and take a pointer to the new var in one line, a shortcut, otherwise you would have to first declare a var `page := Page{}` and then take the pointer `pagePtr := &page`. – Dylan Reimerink Jan 19 '22 at 23:10
  • 1
    "it will mutate the object itself right". Yes that is correct. – Dylan Reimerink Jan 19 '22 at 23:11