Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying reference to pointer or by value

Tags:

go

I think I understand the answer from here but just in case, I want to explicitly ask about the following (my apologies if you think it is the same question, but to me, it feels different on the concerns):

func f() *int {
  d := 6
  pD := new(int)
  pD = &d // option 1
  *pD = d // option 2
  return pD
}

The first option where I just copy the reference as a pointer is performance-wise, more optimal (this is educational guess, but it seems obvious). I would prefer this method/pattern.

The second option would (shallow) copy (?) instead. What I presume is that this method, because it copies, I have no concerns about GC sweeping the instance of 'd'. I often use this method due to my insecurity (or ignorance as a beginner).

What I am concerned about (or more so, insecure about) is that in the first method (where address of 'd' is transfered), will GC recognize that it (the 'd' variable) is referenced by a pointer container, thus it will not be swept? Thus it will be safe to use this method instead? I.e. can I safely pass around pointer 'pD' returned from func 'f()' for the lifetime of the application?

Reference: https://play.golang.org/p/JWNf5yRd_B

like image 637
HidekiAI Avatar asked Feb 14 '17 20:02

HidekiAI


2 Answers

There is no better place to look than the official documentation:

func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    f := File{fd, name, nil, 0}
    return &f
}

Note that, unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. In fact, taking the address of a composite literal allocates a fresh instance each time it is evaluated, so we can combine these last two lines.

(source: "Effective Go")

So the first option (returning a pointer to a local variable) is absolutely safe and even encouraged. By performing escape analysis the compiler can tell that a variable escapes its local scope and allocates it on the heap instead.

like image 122
Agis Avatar answered Sep 30 '22 15:09

Agis


In short: No.

First: There are no "references" in Go. Forget about this idea now, otherwise you'll hurt yourself. Really. Thinking about "by reference" is plain wrong.

Second: Performance is totally the same. Forget about this type of nano optimisations now. Especially when dealing with int. If and only if you have a performance problem: Measure, then optimize. It might be intuitively appealing to think "Handing around a tiny pointer of 8 bytes must be much faster than copying structs with 30 or even 100 bytes." It is not, at least it is not that simple.

Third: Just write it a func f() *int { d := 6; return &d; }. There is no need to do any fancy dances here.

Fourth: Option 2 makes a "deep copy" of the int. But this might be misleading as there are no "shallow copies" of an int so I'm unsure if I understand what you are asking here. Go has no notion of deep vs. shallow copy. If you copy a pointer value the pointer value is copied. You remember the first point? There are no references in Go. A pointer value is a value if copied you have a copy of the pointer value. Such a copy does absolutely nothing to the value pointed to, especially it doesn't do a copy. This would hint that copies in Go are not "deep". Forget about deep/shallow copy when talking about Go. (Of course you can implement functions which perform a "deep copy" of your custom objects)

Fifth: Go has a properly working garbage collector. It makes absolutely no difference what you do: While an object is live it won't be collected and once it can be collected it will be. You can pass, return, copy, hand over, take address, dereference pointers or whatever you like, it just does not matter. The GC works properly. (Unless you are deliberately looking for pain and errors by using package unsafe.)

like image 25
Volker Avatar answered Sep 30 '22 15:09

Volker