LanMan
LanMan

Reputation: 211

Method vs Functions Usage golang

I know differences between func and method. But I am confusing for usages between:

prod:=Product{"title","brand","model"}
prod.Add()

or:

prod:=Product{"title","brand","model"}
products.Add(&prod) // products is package

Upvotes: 3

Views: 3152

Answers (3)

user12817546
user12817546

Reputation:

Extending the fantastic answer by @Danilo:

package main

import "fmt"

type T struct {
    i int
}

func (t *T) F() {
    t = &T{1}
    fmt.Println(t.i)
}

func F(t *T) {
    fmt.Println(t.i)
}

func main() {
    t := T{2}
    (&t).F()
    F(&t)
}

The type of the method func (t *T) F() is the type of the function func F(t *T) with the receiver (t *T) as first argument.

Upvotes: 0

Pandemonium
Pandemonium

Reputation: 8390

These are two distinct cases, one which is a method belongs to Product instance and one is a global function belongs to products package.

type Product struct {
        Title string
        Brand string
        Model string
}

// This method add value to a field in Product
func (p *Product) Add(field, value string) {
        switch field {
        case "Title":
                p.Title = value
        case "Brand":
                p.Brand = value
        case "Model":
                p.Model = value
        }
}

The above provide a method to add value to itself as an instance of Product, i.e.

product1 := &Product{}
product1.Add("Title", "first_title")

The second case is a public function exposed from a product package. In this case, an instance (or a pointer) of a Product must be supplied as an argument.

package products

func Add(p *Product, field, value string) {
        // Same switch-case as above
}

Add function then can be used from any other package.

package main

import (
        "path/to/products"
)

type Product struct {
        // ...
}

func main() {
        product1 := &Product{}
        products.Add(product1, "Title", "first_title")

Normally in your scenario, the first approach is preferred since it encapsulates the functionality of managing its attributes to itself.

The second scenario might be seen as a "class method approach" (for those coming from OOP like Python or Java) where the package is similar to class and the exposed functions similar to class methods which are more generic and can be used across many types which implement the same interface, like so:

package products

// where p is a Product interface
func Add(p Product, field, value string) {
        // Same switch-case as above
}

type Product interface {
        someMethod()
}

And from another package:

package main

import (
        "path/to/products"
)

type Car struct {
        Title string
        Brand string
        Model string
}

type Ship struct {
        // ...
}

type Airplane struct {
        // ...
}

// All types implement `Product` and can be used in `products.Add`
func (c *Car) someMethod() {}
func (s *Ship) someMethod() {}
func (a *Airplane) someMethod() {}

func main() {
       plane := &Airplane{}
       products.Add(plane, "Model", "Boeing-747")
}

Upvotes: 5

Danilo
Danilo

Reputation: 3327

This is expected as per the spec:

The type of a method is the type of a function with the receiver as first argument.

See https://golang.org/ref/spec#Method_declarations

So when you declare the Add method on Product, you get a function that accepts a pointer to a Product as its first argument. So you end up with

func (p *Product) Add() 

being translated to

func Add(p *Product)

So both your calls are valid and end up doing the same

Upvotes: 2

Related Questions