DEV Community

Cover image for Go Slices: Internal Algorithm of Append Function
Ganesh Kumar
Ganesh Kumar

Posted on

Go Slices: Internal Algorithm of Append Function

#go

Hello, I'm Ganesh Kumar. I'm working on git-lrc: a Git hook for Checking AI generated code.AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production.git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.

In my previous post, I explained about how slices work internally in Go.

Also we found capacity of the slice is not always equal to the length of the slice after appending.

Now let's see why this happens and what is the reason behind it.

Appending to a slice

In the official source code of append function you can see here link

There are two cases for appending to a slice.

  1. Appending single element to a slice.
  2. Appending multiple elements to a slice.

Append single element to a slice:

package main

import "fmt"

func main() {
    a := []int{}
    printSlice(a)
    a = append(a, 0)
    printSlice(a)

    a = append(a, 1)
    printSlice(a)
    a = append(a, 2)
    printSlice(a)
    a = append(a, 3)
    printSlice(a)
    a = append(a, 4)
    printSlice(a)
    a = append(a, 5)
    printSlice(a)
    a = append(a, 6)
    printSlice(a)
}
func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
Enter fullscreen mode Exit fullscreen mode

This will output:

gk@jarvis:~/exp/code/rd/go-exmaple$ go run main.go
len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=3 cap=4 [0 1 2]
len=4 cap=4 [0 1 2 3]
len=5 cap=8 [0 1 2 3 4]
len=6 cap=8 [0 1 2 3 4 5]
len=7 cap=8 [0 1 2 3 4 5 6]
Enter fullscreen mode Exit fullscreen mode

We can see clearly on how the capacity is growing.

Whenever there is match with len and cap the next element will be appended with cap double the previous cap.

Internal Algorithm of Append Function


package main

import "fmt"

func main() {
    a := []int{1, 2,}
    printSlice(a)

    a = append(a, 3)
    printSlice(a)

    a = append(a, 4,5,6,7,8,9)
    printSlice(a)

    a =  append(a, 10,11,12)
    printSlice(a) 
}   
func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

Enter fullscreen mode Exit fullscreen mode

Output:

gk@jarvis:~/exp/code/rd/go-exmaple$ go run main.go 
len=2 cap=2 [1 2]
len=3 cap=4 [1 2 3]
len=9 cap=10 [1 2 3 4 5 6 7 8 9]
len=12 cap=20 [1 2 3 4 5 6 7 8 9 10 11 12]
Enter fullscreen mode Exit fullscreen mode

Append 1: Bulk append of 1,2
Inserted length is 2
Capacity is 2

Append 2: Single append of 3
Inserted length is 1
Capacity is 4
Which is double the previous capacity

Append 3: Bulk append of 4,5,6,7,8,9
Inserted length is 6
Capacity is 10
Which is double the previous capacity but increased by 2 to round off to bucket size

Append 4: Bulk append of 10,11,12
Inserted length is 3
Capacity is 20
Which is double the previous capacity

The underlying reasons for this behavior are:

  1. Safety Checks
  2. Zero-Size Shortcut: If the slice is empty, it skips allocation if possible.
  3. Calculating the Target Capacity:
    1. If the current capacity is small (less than 256 elements), it will double the capacity.
    2. If the current capacity is large (256 elements or more), it grows by a formula that smooths the transition from 2.0x to 1.25x. (Note: Before Go 1.18, this threshold was 1024 and grew strictly by 1.25x).
    3. If there is a bulk append and the required capacity is strictly greater than double the current capacity, it jumps straight to the exact required capacity instead and even it round up to the next size class.
  4. Byte Math and Memory Rounding (Size Classes): Single element append: will double the capacity. Bulk append: will calculate the required capacity and round it up to the next size class.

This is capacity growth algorithm of append function is specifically designed to minimize the number of reallocations.

As reallocations are expensive operations.

Conclusion

Now we can understand how the append function works and why the capacity of the slice is not always equal to the length of the slice after appending.

How append function works very fast with increase in number of elements to be appended to it.

git-lrc

πŸ‘‰ Check out: git-lrc
Any feedback or contributors are welcome! It’s online, open-source, and ready for anyone to use.
⭐ Star it on GitHub:

GitHub logo HexmosTech / git-lrc

Free, Unlimited AI Code Reviews That Run on Commit

git-lrc logo

git-lrc

Free, Unlimited AI Code Reviews That Run on Commit


git-lrc - Free, unlimited AI code reviews that run on commit | Product Hunt

AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production.

git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.

See It In Action

See git-lrc catch serious security issues such as leaked credentials, expensive cloud operations, and sensitive material in log statements

git-lrc-intro-60s.mp4

Why

  • πŸ€– AI agents silently break things. Code removed. Logic changed. Edge cases gone. You won't notice until production.
  • πŸ” Catch it before it ships. AI-powered inline comments show you exactly what changed and what looks wrong.
  • πŸ” Build a habit, ship better code. Regular review β†’ fewer bugs β†’ more robust code β†’ better results in your team.
  • πŸ”— Why git? Git is universal. Every editor, every IDE, every AI…




https://github.com/golang/go/blob/master/src/runtime/slice.go

Top comments (0)