Pointer receiver or not

Hi,

I was reading Receiver Type bullet points but got confused a bit. Just to make the question more direct, I’ll use an example.

// EXAMPLE 1

type StructOne struct {
	id   int
	text string
	user StructTwo
	abc []StructThree
}

func (StructOne) method() {}  // Is this correct?
func (*StructOne) method() {} // Is this correct?

// ------------------------------------------

// EXAMPLE 2

type StructOne struct {
	id   int
	text string
	user *StructTwo
	abc []*StructThree
}

func (StructOne) method() {}  // Is this correct?
func (*StructOne) method() {} // Is this correct?

The question is, if a struct contains pointer fields or not, would the function receiver be a pointer receiver or just a normal one? The critical point here is that, do the rules apply to only IMMEDIATE struct fields or/and SUB fields as well? Just one thing before you answer, assume that:

  1. I DO NOT mutate any fields in my functions.
  2. Both StructTwo and StructThree might contain pointer fields as well.

Whether or not the struct contains pointer fields doesn’t matter when choosing struct pointer or value method receivers. Any changes you make to a struct passed by value are “lost” after the function returns (because you’re manipulating a copy of the struct). If that struct contains pointers, then you get a copy of the pointer, so you can modify any data through that pointer.

So in Example 2’s first method, you could do this:

func (s StructOne) method() {
    s.id = 1    // pointless because your copy, `s` will be lost after the function returns.
    s.user.name = "temporary"    // this modifies the data through the s.user pointer.
}

The rest of my answer is just how I think of functions that receive struct values as parameters. Maybe it will be helpful to you or to others:

type A struct {
    s string
    i int
}

type B struct {
    a *A
    f float32
}

func FuncA(a A) { }

func FuncB(b B) { }

You could rewrite functions that receive structs as values as functions that receive the individual fields that make up the struct:

func FuncA(s string, i int) { }

func FuncB(a *A, f float32) { }

Notice how when I “unpacked” B, I kept a *A as a pointer. Passing structs by value doesn’t cause any sort of recursion where inner struct pointers are also passed by value.

Because a *A is a pointer, you can modify the fields of a and those changes will still be there after FuncB returns.

If you needed to execute methods on your A struct or B struct, you could just rebuild the A or B as necessary:

func FuncA(s string, i int) {
    // do something with the "fields"
    data := strings.Repeat(s, i)

    // need to execute some other method on A:
    A{s: s, i: i}.method()

    // do other work...
}

However, if B's definition was:

type B struct {
    a A
    f float32
}

Then because the actual A struct value is embedded into B, you could think of FuncB also getting A's fields:

//          __________ type B ________
//         |___ type A ____          |
//         |              |          |
func FuncB(s string, i int, f float32) {
    // maybe I want to build-up my B struct again:
    b := B{A{s, i}, f}
}

Then modifying s or i would not be persistent after the function returns.

First of all thanks for taking time for answering. However, I explicitly mentioned in my question:

I DO NOT mutate any fields in my functions

so I expected an answer that didn’t focus on mutation/modification etc. at all because my question has nothing to do with it. The reference link I posted already makes it clear so if struct fields are mutated then it has to be pointer receiver. The second part of your answer looks more like a suggestion so don’t really think it answers the question. Anyway, let’s move on for now.

The question was, if a struct X contains pointer fields (whether immediate fields AND/OR fields of embedded structs), should the function X be used as a pointer receiver a copy?

For example there is a bullet point there and reads as follows:

If the receiver is a struct that contains a sync.Mutex or similar synchronizing field, the receiver must be a pointer to avoid copying.

This sounds like, as long as struct X contains a pointer field (as an immediate field or part of embedded struct’s field) function X be used as a pointer receiver, not a copy. However, I am not sure If I should read it this way!

As the original answer stated, if you don’t mutate, then you can use either pointer or value receivers. Whether the struct members are values or pointers is immaterial.

On the other hand, if you do mutate or you have struct members, like mutex that mutate on your behalf, then you must use pointer receivers.

Make sense?

Whether the struct members are values or pointers is immaterial.

This clears the 50% so far.

For the remaining 50% which is related to sync.Mutex, I assume that the example below is correct. Not because the db field is a pointer to sql.DB struct but purely because sql.DB struct has an field called mu which is sync.Mutex). Is my understanding correct? If so, we are 100% here!

package repository

import "database/sql"

type User struct {
	db      *sql.DB
	timeout time.Duration
}

func NewUser(db *sql.DB, timeout time.Duration) *User {
	return &User{
		db:      db,
		timeout: timeout,
	}
}

func (u *User) Persist(user User) error {}
func (u *User) Delete(userID string) error {}
func (u *User) Find(userID string) (User, error) {}
// ...

Since a value receiver is a copy, the mutex wouldn’t work.

I tend to use pointer receivers exclusively. If the methods don’t change anything, no harm. If a method does need to change something, then it works properly (it changes the actual struct members, not a copy … which can be very confusing).

It is easy to try all this out on the playground. That would help solidify things for you

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.