Both of your examples work properly. Also note that instead of new()
, you could also use a composite literal and take its address like this:
var wg = &sync.WaitGroup{}
Methods of sync.WaitGroup
have pointer receiver, so whenever you call its methods, the address of the WaitGroup
struct value is needed. This is not a problem, as when wg
is a non-pointer, the wg.Add(1)
and wg.Done()
calls are shorthand for (&wg).Add(1)
and (&wg).Done()
, so the compiler automatically "rewrites" those calls to take the address of wg
first, and use that address as the receiver of the methods.
However, I still think that if a value is only useful as a pointer (sync.WaitGroup
is a shining example), you should declare it and work with it as a pointer in the first place, so that leaves less room for errors.
For example, if you use a non-pointer and you declare the function to expect a non-pointer, and you pass it as a non-pointer, you will get no compile-time error, yet it will misbehave (sync.WaitGroup
should not be copied).
Although today's linter would give you a warning message, still, I believe it's best to work with pointers all the time.
Another reason to work with pointers: if a function would return a sync.WaitGroup
, or if you have a map that stores sync.WaitGroup
as values, you would not be able to call methods on the result, because return values of functions and map index operations are not addressable. If the function would return a pointer value, or if you would store pointers in the map in the first place, you could still call the methods without having to store them in a local variable. For details, see How can I store reference to the result of an operation in Go?
For example:
func getWg() sync.WaitGroup { return sync.WaitGroup{} }
getWg().Wait() // Compile-time error!
m := map[int]sync.WaitGroup{
1: sync.WaitGroup{},
}
m[1].Wait() // Again: compile-time error
But these work:
func getWg() *sync.WaitGroup { return &sync.WaitGroup{} }
getWg().Wait() // Works, you can call methods on the return value
m := map[int]*sync.WaitGroup{
1: &sync.WaitGroup{},
}
m[1].Wait() // Also works
Read more about this here: Why should constructor of Go return address?