`sync.WaitGroup`的方法是什么?

I have this simple program below

package main

import (
    "fmt"
    "sync"
    "time"
)

var wg sync.WaitGroup

func main() {
    wg.Add(1)

    go func() {
        fmt.Println("starting...")
        time.Sleep(1 * time.Second)
        fmt.Println("done....")
        wg.Done()
    } ()

    wg.Wait()

}

Notice that I use var wg sync.WaitGroup as a value, not a pointer. But the page for the sync package specifies that the Add, Done and Wait function take a *sync.WaitGroup.

Why/How does this work?

The method set of sync.WaitGroup is the empty method set:

wg := sync.WaitGroup{}
fmt.Println(reflect.TypeOf(wg).NumMethod())

Output (try it on the Go Playground):

0

This is because all the methods of sync.WaitGroup have pointer receivers, so they are all part of the method set of the *sync.WaitGroup type.

When you do:

var wg sync.WaitGroup

wg.Add(1)
wg.Done()
// etc.

This is actually a shorthand for (&wg).Add(1), (&wg).Done() etc.

This is in Spec: Calls:

If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m().

So when you have a value that is addressable (a variable is addressable), you may call any methods that have pointer receiver on non-pointer values, and the compiler will automatically take the address and use that as the receiver value.

See related question:

Calling a method with a pointer receiver by an object instead of a pointer to it?

In your case you are modifying a global wg object, if you pass it to a function you have to use pointer because you need to modify the object itself. If you pass by value, inside your function you will be modifying a copy of it, not the object itself.

From the documentation WaitGroup :

A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished.

From your Question

How does this work?

So for Add method is set the number of goroutine your called. From your code you have only one :

func main() {
    wg.Add(1)

    go func() {
        fmt.Println("starting...")
        time.Sleep(1 * time.Second)
        fmt.Println("done....")
        wg.Done()
    } ()

    wg.Wait()

}

so it will tells goroutine to wait and print the result. as for wg.Done() this is for telling the one gouroutine has finished to work. and it tells to the add to decrement to -1. You can see the code below How Done method works :

// Done decrements the WaitGroup counter.
func (wg *WaitGroup) Done() {
    wg.Add(-1)
}

and finally for the Wait method this is for blocking goroutine until the WaitGroup counter is zero.

And another:

Why ?

from your code above if you don't use WaitGroup you will not be able to print the result from the goroutine.

All in all you can read it in the documentation.