Assigning to an index
The elements of a slice are variables, and you can assign to them whenever you like. The syntax is the same as for arrays: the slice's name, the index in square brackets, then = and the new value.
names := []string{"Andy", "Mary", "Cody"}
names[0] = "Mircea"
names[2] = "Mike"
fmt.Println(names)
[Mircea Mary Mike]
Two assignments replaced two elements, and the element at index 1 was left alone. The slice has the same length as before. Assigning to an index replaces a value; it never inserts or removes one. To add an element you use append, and to shorten a slice you re-slice it.
The new value must have the slice's element type. Assigning a number to an element of a []string is a compile error.
Updating from the old value
To change an element based on what it holds now, read it on the right-hand side of the assignment:
ages := []int{14, 32, 50, 21}
ages[1] = ages[1] + 5
fmt.Println(ages)
[14 37 50 21]
Go evaluates the right-hand side first, so ages[1] + 5 is 37, and that value is then written back into ages[1]. Go has a shorter form for this pattern: ages[1] += 5 means exactly the same. There are matching operators for the other arithmetic: -=, *=, /= and %=. For adding or subtracting 1, use ages[1]++ or ages[1]--.
The exercise
You start from these slices:
fruits := []string{"mango", "kiwi", "grapefruit"}
ages := []int{14, 32, 50, 21}
- Change the first fruit to
"apple"by assigning to index 0. - Add 5 to the second age by assigning
ages[1] + 5back toages[1].
fruits[0] = "apple"
ages[1] = ages[1] + 5
Afterwards fruits is apple, kiwi, grapefruit, and ages is 14, 37, 50, 21. The second item has index 1 because counting starts at 0, and the same zero-based numbering applies when you write to an element as when you read from it.
Slices share their elements
A slice does not own its elements. It refers to an underlying array. When you pass a slice to a function, or assign it to another variable, both names refer to the same elements, so an update through one is visible through the other:
func bump(s []int) { s[0] += 100 }
bump(ages)
fmt.Println(ages)
[114 37 50 21]
This is the opposite of arrays, which are copied when passed. It is handy when a function should modify the data in place, and surprising when you expected a private copy. If you do need independent data, make a new slice of the same length and use copy:
dup := make([]int, len(ages))
copy(dup, ages)
dup[0] = 0
fmt.Println(ages, dup)
[14 32 50 21] [0 32 50 21]
Now dup has its own elements, and changing it does not affect ages.
The same sharing applies to a part of a slice. After part := ages[1:3], the assignment part[0] = 0 sets ages[1] to 0, because index 0 of part is index 1 of ages.
Updating every element
To change every element, loop over the indices and assign through the slice:
for i := range ages {
ages[i] *= 2
}
Writing for _, a := range ages { a *= 2 } would not work. The variable a is a copy of each element, so doubling it leaves the slice unchanged. The code compiles and runs, and the slice prints exactly as before.
Common mistakes
- Using an index that does not exist. Assigning to
ages[4]on a four-element slice panics when it runs. A slice does not grow by assignment: useappend. - Off-by-one indexing. The first item is index 0.
- Updating the loop variable instead of the slice. In a
rangeloop, the value variable is a copy. Assign throughages[i]when the slice itself must change. - Forgetting that data is shared. Changing a slice inside a function changes the caller's data.
Key takeaways
s[i] = valuereplaces one element and keeps the length.s[i] += nis shorthand fors[i] = s[i] + n.- Slices share their underlying array, so updates are visible everywhere the slice is used.
- Loop with the index to update every element.