DEV Community

Cover image for Go: Arrays and Slices, a deep dive.

Go: Arrays and Slices, a deep dive.

Yussif Mohammed on January 04, 2023

Welcome, In this article we're going to discuss all there is to know about arrays and slices in Go, we'll go deep talking about their internal st...
Collapse
 
jhelberg profile image
Joost Helberg

Great in depth discussion on a less understood aspect of Go. Thanks!

Collapse
 
dawkaka profile image
Yussif Mohammed

Thank you so much for your kind words

Collapse
 
amadu9933 profile image
Amadu Hamza

very helpful

Collapse
 
telemachus profile image
Peter Aronoff • Edited

Re "Potential Quirks of Slices", I think you may be wrong about regexp.Find holding onto memory the way you describe. This used to be a problem, and if you read an earlier Go blog post about slices, you may think it still is a problem. But it hasn't been a problem for a while.

The old version of regexp.Find looked like this (note the last line especially):

// Find returns a slice holding the text of the leftmost match in b of the regular expression.
// A return value of nil indicates no match.
func (re *Regexp) Find(b []byte) []byte {
    a := re.doExecute("", b, 0)
    if a == nil {
        return nil
    }
    return b[a[0]:a[1]]
}
Enter fullscreen mode Exit fullscreen mode

The new version looks like this (again, the last line is key):

// Find returns a slice holding the text of the leftmost match in b of the regular expression.
// A return value of nil indicates no match.
func (re *Regexp) Find(b []byte) []byte {
    var dstCap [2]int
    a := re.doExecute(nil, b, "", 0, 2, dstCap[:0])
    if a == nil {
        return nil
    }
    return b[a[0]:a[1]:a[1]]
}
Enter fullscreen mode Exit fullscreen mode

Because the new version uses a full slice expression, I don't think that it holds onto memory any longer. The third item in a full slice expression specifies the maximum capacity of the slice. The definition states that the capacity of the resulting slice is no greater (in this case) than a[1] - a[0]. That means that the capacity will be no longer than the length of the string found, in your case gopher.

Full slice expressions were introduced in December of 2013, and regexp.Find was fixed sometime after that. This is great to know about because we can also use full slice expressions to control how much of a backing array is exposed by a slice. This can protect us from blocking the garbage collector, as here, and it can also prevent the caller from being able to (accidentally or intentionally) changing parts of the backing array that they shouldn't have access to. (That is, in the old version of regexp.Find, the calling code would have access to a lot more of the 1 GB document in your example than just the word "gopher." In the new version, that's protected against.)

Collapse
 
dawkaka profile image
Yussif Mohammed

I did not know that
Thanks for the correction!

Collapse
 
dulitha_kularathne_40aa07 profile image
Dulitha Kularathne

I did not quite understand this.
Even full slicing doesnt do a deep copy. It still exposes the same underlying array.
For instance, int the following program , editing s would also edit the b1 slice.
I am using go 1.23 version.

package main
import (
    "fmt"
    "regexp"
)
func main() {
    b1 := []byte(`seafood fool`)
    re := regexp.MustCompile(`foo.?`)
    s := re.Find(b1)
    s[1] = 127
    fmt.Println("%q\n", s)
    fmt.Println("%q\n", b1)

}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
kevburnsjr profile image
Kevin Burns

Error on line 14 of codeblock 11

    fmt.Println(countries) // => [Chian India Kenya]
Enter fullscreen mode Exit fullscreen mode

Should read

    fmt.Println(countries) // => [China India Kenya ]
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dawkaka profile image
Yussif Mohammed

Thanks for catching that.

Collapse
 
valentintt profile image
ValentinTT

Hi! Nice article. Just a comment: "len is a built-in function that returns the number of items in an array of slice" shouldn't be "in an array or slice"?

Collapse
 
donjoe profile image
DonJoe

Uh oh... this part might need revising:

If we try to change element an index greater than or equal to the arrays length the code will won't compile it'll panic with an index out of bound error

Collapse
 
ahua52 profile image
Renhua Liu

Image description
Hi, the result is 3,4
cap(subSlice) = 4

Collapse
 
naresh_3312f7fb86ee47c8ed profile image
Naresh • Edited

i my case fmt.Println(len(subSlice), cap(subSlice)), gives 3 5. Looks like the subSlice is retaining the slice length, when we ommit it during creation

Collapse
 
ayoubak141 profile image
Phantom II

very helpful. Thanks!