1

I run this function:

func Run() () {
    // This slice is going to be filled out by a channel and goroutine.
    vertices := make([]Vertex, 0)

    var wg sync.WaitGroup

    // Obtain a writer to fill out the vertices.
    writer := Writer(&wg, vertices)

    // Run an arbitrary logic to send data to writer.
    Logic(writer)

    // Stop the writer reading on the channel.
    close(writer)

    // Wait for the write to complete.
    wg.Wait()

    // See if vertices slice is actually filled out.
    DoublecheckVertices(vertices)
}

But eventually, my vertices slice is empty:

func DoublecheckVertices(vertices []Vertex) () {
    // Here I notice that `vertices` slice is actually empty :(

}

The function which returns the writer is something like this:

func Writer(wg *sync.WaitGroup, vertices []Vertex) (chan<- []*Triangle3) {
    // External code writes to this channel.
    // This goroutine reads the channel and writes to vertices.
    writer := make(chan []*Triangle3)

    // Write by a goroutine.
    wg.Add(1)
    go func() {
        defer wg.Done()

        a := Vertex{}

        // Read from the channel and write them to vertices.
        for ts := range writer {
            for _, t := range ts {
                a.X = float32(t.V[0].X)
                a.Y = float32(t.V[0].Y)
                a.Z = float32(t.V[0].Z)
                vertices = append(vertices, a)
            }
        }
    }()

    return writer
}

Can anybody help me with figuring out why my vertices slice is eventually empty?

Logs

Logs indicate that the vertices slice is actually filled out. But for some reason, it's empty when it's passed to DoublecheckVertices.

                vertices = append(vertices, a)
                // This Log shows the slice is actually filled out:
                fmt.Printf("vertices len() is %v\n", len(vertices))
Megidd
  • 7,089
  • 6
  • 65
  • 142

1 Answers1

1

That seems similar to "Pass slice as function argument, and modify the original slice"

If you want your goroutine to modify the slice you created outside, you would need a pointer to that slice:

func Writer(wg *sync.WaitGroup, vertices *[]Vertex) (chan<- []*Triangle3) {
    // External code writes to this channel.
    // This goroutine reads the channel and writes to vertices.
    writer := make(chan []*Triangle3)

    // Write by a goroutine.
    wg.Add(1)
    go func() {
        defer wg.Done()

        a := Vertex{}

        // Read from the channel and write them to vertices.
        for ts := range writer {
            for _, t := range ts {
                a.X = float32(t.V[0].X)
                a.Y = float32(t.V[0].Y)
                a.Z = float32(t.V[0].Z)
                *vertices = append(*vertices, a)  <=====
            }
        }
    }()

    return writer
}
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • It's fixed. But I thought Golang would pass a slice by just a small header and its underlying data. – Megidd Jan 15 '23 at 09:40
  • I thought that simply passing a slice would be enough, according to this, but I was wrong: https://stackoverflow.com/a/39993797/3405291 – Megidd Jan 15 '23 at 09:43
  • 2
    @user3405291 A slice, like everything else, is [passed by value](https://stackoverflow.com/a/39993797/6309). I suspect the `append(yourSlice, anElement)`, which recreates a slice with one more element, would change the value. – VonC Jan 15 '23 at 09:45
  • Right. Actually I didn't study [that post](https://stackoverflow.com/a/39993797/3405291) thoroughly, my bad: `If the function adds new elements to the slice, that requires changing the slice header, which the caller will not see.` – Megidd Jan 15 '23 at 09:47
  • 1
    @user3405291 If your slice was pre-allocated with a known quantity of elements, *then* you might use `[]T` instead of `*[]T`: you would stored your `Vertex` in `vertices[0]` or `vertices [1]` or .... That should work. – VonC Jan 15 '23 at 09:49
  • Right, makes sense. – Megidd Jan 15 '23 at 09:51