Appending to a slice of slices

I’m currently engaged in writing my second Go program - the first was a modified form of Hello World called Hello Pete - and I have encountered an aspect of slices which I am struggling to understand. I’m hoping that someone here will be kind enough to assist with an explanation.

I’m looking to read a csv file one record at a time, extract a subset of the fields and use them to populate a slice of string slices. My problem lies in the final part of the process.

I’ve tried to reproduce the behaviour I am seeing both here and in the playground.

package main

import (
	"fmt"
)

func main() {
	mySOS := make([][]string, 1)
	fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
	
	row0 := []string{"Twas", "brillig", "and", "the", "slithy", "toves"}
	row1 := []string{"did", "gyre", "and", "gimble", "in", "the"}
	row2 := []string{"wabe", "all", "mimsey", "were", "the", "borogoves"}
	// row3 := []string{"and", "the", "mome", "raths", "outgrabe", "."}

	s := make([]string, 3)
	
	s[0] = row0[2]
	s[1] = row0[3]
	s[2] = row0[4]
	
	fmt.Printf("s is of type %T and has value %v\n", s, s)
	mySOS = append(mySOS, s)
	fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
	
	s[0] = row1[2]
	s[1] = row1[3]
	s[2] = row1[4]
	
	fmt.Printf("s is of type %T and has value %v\n", s, s)
	mySOS = append(mySOS, s)
	fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
	
	s[0] = row2[2]
	s[1] = row2[3]
	s[2] = row2[4]
	
	fmt.Printf("s is of type %T and has value %v\n", s, s)
	mySOS = append(mySOS, s)
	fmt.Printf("mySliceOfSlices is of type %T and has value %v\n", mySOS, mySOS)
}

https://play.golang.org/p/o0ufVU3sBzR

My puzzlement is over the fact that mySOS always ends up being fully populated with the final slice from the test data. After spending a considerable amount of time staring at it I tried a slightly different approach which can be seen here.

package main

import (
	"fmt"
)

func main() {
	mySOS := make([][]string, 1)
	fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
	
	row0 := []string{"Twas", "brillig", "and", "the", "slithy", "toves"}
	row1 := []string{"did", "gyre", "and", "gimble", "in", "the"}
	row2 := []string{"wabe", "all", "mimsey", "were", "the", "borogoves"}
	// row3 := []string{"and", "the", "mome", "raths", "outgrabe", "."}
	
	/*
	s := make([]string, 3)
	
	s[0] = row0[2]
	s[1] = row0[3]
	s[2] = row0[4]
	*/
	
	// fmt.Printf("s is of type %T and has value %v\n", s, s)
	mySOS = append(mySOS, row1[2:5])
	fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
	
	/*
	s[0] = row1[2]
	s[1] = row1[3]
	s[2] = row1[4]
	*/
	
	// fmt.Printf("s is of type %T and has value %v\n", s, s)
	mySOS = append(mySOS, row0[2:5])
	fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
	
	/*
	s[0] = row2[2]
	s[1] = row2[3]
	s[2] = row2[4]
	*/
	
	// fmt.Printf("s is of type %T and has value %v\n", s, s)
	mySOS = append(mySOS, row2[2:5])
	fmt.Printf("mySliceOfSlices is of type %T and has value %v\n", mySOS, mySOS)
}

https://play.golang.org/p/nbWWEjHTzEX

This time mySOS contains the data I was expecting. However I still cannot see why the result of the initial code differs.

As previously stated I would be grateful if anyone can help clear up my obvious misunderstanding.

In your first example you are continuously redefining the slice s[:3] so that it ends up pointing to the last set of values. You need a new backing array for each “s” slice. See: https://play.golang.org/p/Vz1b8afT_eJ

Ah, OK. So would I be correct in thinking then that it is a reference to s - sticking with the first example - which is appended to mySOS, as opposed to the value?

A slice “value” includes a pointer to the “backing array.” (See “Slice Internals” at https://blog.golang.org/go-slices-usage-and-internals.)

So, yes, you’re appending references to the backing array that you’re continually updating through the slice reference.

Got it. Many thanks for both that, and also the link to the well written and lucid article.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.