Bummer! This is just a preview. You need to be signed in with a Basic account to view the entire video.
Start a free Basic trial
to watch this video
Slices, like arrays, also represent a list of elements. The code to update and access a slice's elements looks much the same as the code to work with an array's elements. But slices a little easier to work with than arrays, once you know the proper techniques. So slices are more commonly used than arrays.
Slices, like arrays, also represent a list of elements. The code to update and access a slice's elements looks much the same as the code to work with an array's elements. But slices a little easier to work with than arrays, once you know the proper techniques. So slices are more commonly used than arrays.
A slice doesn't actually store anything itself. It references a portion of an underlying array that actually holds the data. Most of the time, you won't have to worry about the underlying array. But it can cause some suprising bugs if you don't know how it works.
We'll start by creating an array, and then basing some slices off of it. This may not be the easiest way to use slices in your programs, but it does demonstrate what's going on.
Here we have an array with 5 elements. We'll create 2 slices based on it. The first will include index 0 of the array up until index 3 (but not including index 3). The second will include index 2 up until index 5 (but not including index 5, which is a good thing, because the array doesn't have an index 5).
package main
import "fmt"
func main() {
a := [5]int{0, 1, 2, 3, 4}
s1 := a[0:3]
s2 := a[2:5]
fmt.Println(a, s1, s2)
}
If we run this, it will print the underlying array, as well as the two slices: [0 1 2 3 4] [0 1 2] [2 3 4]
. Notice that the number at index 2
in the underlying array appears in both slices.
Now, let's try modifying that number in the underlying array. Then we'll print the array and both slices again.
a[2] = 88
fmt.Println(a, s1, s2)
If we run it again, we'll see that the underlying array has been updated. But it also appears that the contents of both slices have changed. That's because, as we mentioned before, slices don't actually hold any data themselves. They're just a sort of window into the contents of the underlying array.
We can use slices to update the underlying array as well. Let's update the element at index 0 of the second slice, which is actually the element at index 2 of the underlying array. Then we'll print the array and both slices again.
s2[0] = 999
fmt.Println(a, s1, s2)
Run it again, and we'll see that the element at index 2 of the underlying array has been updated, and so both slices reflect the change as well.
- We can re-slice a slice to reveal more of the underlying array.
s1 = s1[0:4]
fmt.Println(a, s1, s2)
- If we try the same with the second slice, though, we'll get a runtime panic:
s2 = s2[0:4]
fmt.Println(a, s1, s2)
- That's because we tried to extend the slice beyond the end of the underlying array.
- There are a couple functions useful for getting information about slices.
-
len
gets the current length of the slice, just as it does when used with an array. -
cap
shows the capacity of the slice, which is usually the number of elements between the start of the slice and the end of the underlying array.
-
fmt.Println("len(s1):", len(s1), "cap(s1):", cap(s1))
fmt.Println("len(s2):", len(s2), "cap(s2):", cap(s2))
- Notice
s1
's capacity is higher thans2
's.s2
doesn't have any room to grow because it's right at the end of the underlying array.
So what can we do if we need to add a value to a slice, but we're at the end of the underlying array? Go offers a built-in function called append
that can add new values to a slice, even if it's already at its capacity. Let's call append
with our s2
slice, and append the value 5
to it. Now, append
doesn't actually modify the existing slice, it returns a new slice with the same contents as the old one, plus the new values appended at the end. So we'll need to assign that return value back to the s2
variable. Then, we'll print everything out again so we can look at the changes.
s2 = append(s2, 5)
fmt.Println(a, s1, s2)
If we look at the output, we'll see 5
there on the end of s2
.
Now, the question is, how did append
add a value onto s2
if the underlying array was already full? Arrays can't grow and shrink like slices can. The answer is that append
created a new, bigger array, copied all the elements from the shorter array into it, and then returned a new slice that points to the bigger array. We can see this if we print out the capacities of s1
and s2
again:
fmt.Println("len(s1):", len(s1), "cap(s1):", cap(s1))
fmt.Println("len(s2):", len(s2), "cap(s2):", cap(s2))
If we run this, we'll see that s2
's capacity is now even higher than s1
's because it's pointing at a bigger array. And even though we appended just one element, s2
's capacity increased by 3, not by 1. That's because allocating an array is slow, so append
gives us more room than we've asked for, in case we're going to do more appends later. If append
runs out of capacity again, it will allocate an even bigger underlying array next time.
I mentioned before that creating an array and then basing slices off of it may not be the easiest way to use slices in your programs. It's also not the safest. Now that you have a better understanding of how slices work, I'm going to show you a better way.
We showed you before how to pre-populate an array using curly braces: a := [3]int{1, 2, 3}
. You can create a pre-populated slice using almost the same notation; you just leave off the size within the square brackets: s := []int{1, 2, 3}
. An underlying array will automatically be created for the slice, so you don't have to worry about where to store it, or worry about accidentally altering it. If you don't want to specify starting element values, you can leave those out: s := []int{}
.
Once you have the slice, you can then append values as needed: s = append(s, 4, 5)
, s = append(s, 6, 7, 8)
, and so on. We can print the slice's contents: fmt.Println(s)
, and then run it, and we'll see that all the appended values have accumulated in the slice. We don't have to worry about the underlying array at all. It just works. Until you have more specific needs, you'll probably find that this is the best way to create lists of items in Go.
package main
import "fmt"
func main() {
s := []int{1, 2, 3}
s = append(s, 4, 5)
s = append(s, 6, 7, 8)
fmt.Println(s)
}
Just as with arrays, you can use a for ... range
loop to process each element in a slice:
package main
import "fmt"
func main() {
s := []int{1, 2, 3}
for i, v := range s {
fmt.Println("element:", i, "value:", v)
}
}
-
0:00
Sizes like arrays, also represent a list of elements.
-
0:04
The code to update and access the slices element looks much the same as the code to
-
0:08
work with in arrays elements.
-
0:09
But slices are a little easier to work with than arrays,
-
0:12
once you know the proper techniques.
-
0:14
So, slices are more commonly used than arrays.
-
0:17
A slice doesn't actually store anything by itself.
-
0:20
It references a portion of an underlying array that actually holds the data.
-
0:24
Most of the time you won't have to worry about the underlying array, but
-
0:28
it can cause some surprising bugs if you don't know how it works.
-
0:31
So we're going to take a few minutes to show you the details.
-
0:34
We'll start by creating an array and then basing some slices off of it.
-
0:39
This may not be the easiest way to use slices in your programs, but
-
0:42
it does demonstrate what's going on.
-
0:44
Here we have an array with five elements.
-
0:47
We'll create two slices based on it.
-
0:49
The first will include index 0 of the array up until index 3,
-
0:54
but including index 3.
-
0:55
The second will include index 2 up until index 5, but not including index 5,
-
1:00
which is a good thing because the array doesn't have an index 5.
-
1:04
If we run this, it'll print the underlying array, as well as the two slices.
-
1:13
Notice that the numbered index 2 in the underlying array appears in both slices.
-
1:19
Now let's try modifying that number at the underlying array at index 2.
-
1:23
Then we'll print the array in both slices, again.
-
1:26
So we'll say array index 2 = and we'll assign it the number 88.
-
1:31
Try re-running that and we'll see that the implemented index, two of
-
1:36
the underlying array has been updated and so both slices reflect the change as well.
-
1:43
We can re-slice a slice to reveal more of the underlying array.
-
1:47
So we can take the s1 slice, and make a slice of that slice.
-
1:54
So we can start at index 0 of slice 1, and
-
1:58
go up to index 4 which is 1 longer than the existing slice.
-
2:04
And if we try running this, we see that the resulting slice is one longer than
-
2:08
before, and reveals one more element from the underlying array than it used to.
-
2:13
If we try to do the same thing with the second slice though we'll
-
2:16
run into problems.
-
2:17
If we were to say that s2 equals a reslice of s2[0:4] save that and re-run it.
-
2:27
This time we get a runtime panic saying that the slice bounds are out of range.
-
2:32
That's because we tried to extend the slice beyond the end of
-
2:35
the underlying array.
-
2:36
There are a couple of functions that are useful for
-
2:38
getting information about slices that will let us avoid that sort of situation.
-
2:43
Just as it does for arrays, the len built in function returns the length of a slice.
-
2:49
The cap built in function returns the capacity of the slice.
-
2:53
This is usually the number of elements between the start of the slice and
-
2:56
the end of the underlying array.
-
2:58
So, we've restored our original code and added a couple print line statements that
-
3:03
print out the length and capacity of our two slices.
-
3:07
Notice that s1's capacity is higher than s2's.
-
3:11
S2 doesn't have any room to grow because it's right at the end of
-
3:14
the underlying array.
-
3:16
So what can we do if we need to add a value to a slice but
-
3:19
we're at the end of the underlying array?
-
3:21
Go offers a built in function called append that can add new values to a slice
-
3:26
even if it's already at it's capacity.
-
3:28
Let's call append with our s2 slice, and append the value 5 to it.
-
3:35
Now append doesn't actually modify the existing slice, it returns a new slice
-
3:40
with the same contents as the old one plus the new values appended at the end.
-
3:44
So it's very important that we assign that return value back to the S2 variable.
-
3:49
Then, we'll print everything out again so we can look at the changes.
-
3:52
So I'll just copy this print line statement from up above
-
3:55
which prints the underlying array as well as the s1 and s2 slices.
-
4:00
Let's try running this.
-
4:03
And if we look at the output we'll see the number 5 there on the end of the s2 slice.
-
4:09
Now the question is, how did append add a value onto the s2 slice if
-
4:13
the underlying array was already full?
-
4:16
Arrays can't grow and shrink like slices can.
-
4:18
The answer is that the pen created a new bigger array,
-
4:22
copied all the elements from the shorter array into it and
-
4:25
then returned a new slice that points to the bigger array.
-
4:29
We can see this if we print out the capacities of the s1 and s2 slices.
-
4:33
So I will copy these two print line falls down here, and run it again.
-
4:40
And we can see that the s2 slice's capacity is now even bigger than the s1
-
4:44
slice's.
-
4:45
This is because it's pointing at a bigger array, and
-
4:48
even though we appended just one element, s2's capacity increased by 3, not by 1.
-
4:54
That's because allocating an array is slow.
-
4:56
So append gives us more room than we've asked for
-
4:59
in case we're going to more appends later.
-
5:01
If append runs out of capacity again,
-
5:03
it will allocate an even bigger underlying array next time.
-
5:07
You might be wondering why I'm showing you all of this stuff regarding underlying
-
5:10
arrays.
-
5:11
The answer is that I don't want you to be surprised by behavior like what I'm about
-
5:15
to demonstrate.
-
5:16
I've got rid most of our experimental code leaving only the array,
-
5:20
the two slices based on it, and the call to append.
-
5:23
Now I'm going to do as we did before and modify an element on one of the slices.
-
5:27
I'll do slice 2.
-
5:29
Element 0, first element, and
-
5:31
I'll assign some completely different number to that, like 88.
-
5:35
Notice that I'm doing this before a call to a pimp, then as before,
-
5:39
I'll print out the array and the two slices based on it.
-
5:42
Let's try running this.
-
5:45
As before, you can see that our changes reflected not just in the slice
-
5:49
where we made it, but in the other slice, and in the underlying array as well.
-
5:53
Now I'm going to make a change to that same element of the second slice, but
-
5:57
I'm going to do it after the call to append.
-
6:00
So s2 element 0, first element, equals and
-
6:03
we'll do some completely different number again, 999, and
-
6:08
as before, we'll print everything out again Save that, run it again.
-
6:17
When we run it this time, you can see that the second slice was updated, but
-
6:21
the underlying array and the first slice were not.
-
6:26
Remember we said that append will create a new underlying array if it needs to?
-
6:30
That's what happened here.
-
6:31
The s2 slice is now using a completely different underlying array than the s1
-
6:35
slice.
-
6:36
And so changes in one place are no longer reflected in the other.
-
6:41
I mentioned before the creating an array and then basing slices off of it
-
6:44
may not be the easiest way to use slices in your programs.
-
6:48
It also isn't the safest as we just demonstrated.
-
6:51
Now that you have a better understanding of how slices work I
-
6:54
am going to show you a better way.
-
6:55
We showed you before how to pre-populate an array using curling phrases,
-
6:59
we can say something like array equals a three element array of int
-
7:08
items consisting of the values one, two, and three.
-
7:15
You can create a prepopulated slice using almost the same notation.
-
7:19
You just leave off the size within the square brackets.
-
7:24
An underlying array will automatically be created for the slides so you don't
-
7:28
have to worry about where to store it or worry about accidentally altering it.
-
7:32
If you don't want to specify a starting element of values,
-
7:34
you can just leave those out by leaving the curly braces empty.
-
7:40
So since we're creating a slice here and not an array,
-
7:43
I'm gonna rename the variable to s for slice.
-
7:46
Once you have a slice, you can then append values as needed.
-
7:49
So you can s = append (s,
-
7:54
4, 5), for example.
-
7:59
We can append more with s=append(s,
-
8:03
6, 7, 8) and so on.
-
8:07
If we print the slice now, format print line s and run this.
-
8:18
You can see that, all of the appended values have accumulated in the slice.
-
8:22
We don't have to worry about the underlying array at all, it just works.
-
8:26
Until you have more specific needs,
-
8:27
you'll probably find that, this is the best way to create list of items and go.
-
8:31
One more thing to know when working with slices, just as with the arrays,
-
8:35
you can use a for range loop, it's a loop over each item within the slice.
-
8:40
It'll assign the index of the element to the first variable you set up.
-
8:44
And the value that's held at that index to the second variable you set up.
-
8:50
So here,
-
8:50
if we print each element as it comes up we see elements 0 is set to a value of 1.
-
8:55
Element 1 is set to a value of 2.
-
8:58
And element 2 is set to a value of 3.
-
9:00
Arrays and slices are fine to use when you know you're going to be
-
9:03
processing every element they contain in the same way.
-
9:06
You just start at the beginning and move to the end.
-
9:09
But what if you frequently need to look up a value within an array or slice?
-
9:13
You'd have to look through every element it contains to find the one you want.
-
9:16
That's why Go offers Max.
-
9:18
They like to store values under keys that can be used to quickly retrieve
-
9:22
a particular element.
-
9:23
We'll look at maps next.
You need to sign up for Treehouse in order to download course files.
Sign up