-1

Consider the following struct:

type Queue struct {
  Elements []int
}

What would be the different between:

func NewQueue() Queue {
  queue := Queue{}
  return queue
}

and

func NewQueue() *Queue {
  queue := &Queue{}
  return queue
}

To me the seem practically the same, (and in fact trying it with some enqueuing and dequeueing yields the same results) but I still see both usages in the wild, so perhaps one is preferable.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
n_x_l
  • 1,552
  • 3
  • 17
  • 34
  • Short answer: you can return either, but I think you usually want to return the form that you want people to use the most. So often when it's a small type like `time.Time` you return a value and otherwise a pointer. Saying something more detailed in an answer. – twotwotwo Nov 13 '16 at 18:30

1 Answers1

2

It's possible to return a value and then the caller to call methods that have a pointer receiver. However, if the caller is always going to want to use pointers, because the object's big or because methods need to modify it in place, you might as well return a pointer. Pointers vs. values is a common question in Go and there's an answer trying to break down when to use one or the other.

In the specific case of a slice-backed Queue type, it's pretty small and fast to copy as a value, but if you want to be able to copy it around and have everyone see the same data whichever copy is accessed, you're going to need to use a pointer, because a slice is really a little struct of start pointer, length, and capacity, and those change when you reslice or grow it. If this is a surprise, the Go blog posts on the mechanics of append and slice usage and internals could be useful reading.

If your queue isn't for sharing or passing around but for using locally in a single function, you could provide an append-style interface where operations return a modified queue, but at that point maybe you just want to use slice tricks directly.

(If your queue is meant to be used concurrently, think hard about using a buffered channel. It might not be exactly what you're imagining, but a lot of the tricky bits have already been figured out for you by the implementers.)

Also--if Queue is really a slice with methods added, you can make it type Queue []int.

Community
  • 1
  • 1
twotwotwo
  • 28,310
  • 8
  • 69
  • 56
  • Thank you for the detailed response. I might grow my queue in the next few days so I'll keep it as a struct. – n_x_l Nov 13 '16 at 19:04
  • Oops clicked send too soon. The thing that I don't understand is: if what I am returning above is different for the two cases, then how come usage afterwards is the same? That is, whatever `queue` I end up initializing, I am using it the same, with no regard to whether it is a value or a pointer, which is why I wondered if the distinction is worth it. – n_x_l Nov 13 '16 at 19:05
  • Usage isn't the same; Go hides the `.` vs `->` distinction that there is in C++, but other differences remain. For example, after `q := NewQueue()`, to pass `q` to `func f (q *Queue)` you need to use `f(&q)` if `q` is a value but just `f(q)` if it's a pointer. Similarly you can't assign between pointers and values without explicit `&`/`*`, if you're trying to store a `Queue` or `*Queue` in a global or such. – twotwotwo Nov 13 '16 at 19:14
  • Got it, the key here is the hiding of the distinction. Thank you. – n_x_l Nov 13 '16 at 19:22