<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Anu Madhav</title>
    <description>The latest articles on Forem by Anu Madhav (@anu_madhav_62b44758e55af7).</description>
    <link>https://forem.com/anu_madhav_62b44758e55af7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3349893%2Fb8279b32-ceff-4f47-ac76-6c7f25f28a1b.jpg</url>
      <title>Forem: Anu Madhav</title>
      <link>https://forem.com/anu_madhav_62b44758e55af7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/anu_madhav_62b44758e55af7"/>
    <language>en</language>
    <item>
      <title>Understanding Go Slices: Why They Behave Unexpectedly</title>
      <dc:creator>Anu Madhav</dc:creator>
      <pubDate>Sun, 13 Jul 2025 03:48:17 +0000</pubDate>
      <link>https://forem.com/anu_madhav_62b44758e55af7/understanding-go-slices-why-they-behave-unexpectedly-52db</link>
      <guid>https://forem.com/anu_madhav_62b44758e55af7/understanding-go-slices-why-they-behave-unexpectedly-52db</guid>
      <description>&lt;p&gt;&lt;strong&gt;The Slice Structure&lt;/strong&gt;&lt;br&gt;
A slice in Go is not just an array - it's a data structure that contains three components:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;type slice struct {&lt;br&gt;
    array unsafe.Pointer  // pointer to underlying array&lt;br&gt;
    len   int             // length of slice&lt;br&gt;
    cap   int             // capacity of slice&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This structure is the key to understanding slice behavior. When you create a slice, you're creating a header that points to an underlying array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Unexpected Behavior&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider this code that demonstrates the surprising behavior:&lt;/p&gt;

&lt;p&gt;`package main&lt;/p&gt;

&lt;p&gt;import "fmt"&lt;/p&gt;

&lt;p&gt;func main() {&lt;br&gt;
    cart := []string{"Apple", "Banana", "Orange"}&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Appending value into the slice
cart = append(cart, "Milk")
fmt.Println("cart", cart)

// Slicing the slice array
fmt.Println("[:3]", cart[:3])

fruit := cart[:3]
fruit = append(fruit, "lemon")
fmt.Println("fruit", fruit)
fmt.Println("cart", cart)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;cart [Apple Banana Orange Milk]&lt;br&gt;
[:3] [Apple Banana Orange]&lt;br&gt;
fruit [Apple Banana Orange lemon]&lt;br&gt;
cart [Apple Banana Orange lemon]&lt;/p&gt;

&lt;p&gt;Wait, what? Why did cart change when we only modified fruit?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's Really Happening&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Initial State&lt;/strong&gt;&lt;br&gt;
When cart is created with 4 elements, Go allocates an underlying array with enough capacity (likely 4 or more).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Slicing Operation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fruit := cart[:3]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This creates a new slice header fruit that points to the same underlying array as cart. The fruit slice just has a different length (3 instead of 4).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Append Operation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fruit = append(fruit, "lemon")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since fruit has length 3 but the underlying array has capacity for 4 elements, append doesn't need to allocate a new array. It &lt;br&gt;
simply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Places "lemon" at index 3 of the existing array&lt;/li&gt;
&lt;li&gt;Updates the fruit slice's length to 4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Shared Memory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both cart and fruit point to the same underlying array, so when the array is modified, both slices reflect the change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visualizing Memory Layout&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Initial cart: [Apple][Banana][Orange][Milk]&lt;br&gt;
                ↑&lt;br&gt;
             cart points here (len=4, cap=4)&lt;/p&gt;

&lt;p&gt;After fruit := cart[:3]:&lt;br&gt;
[Apple][Banana][Orange][Milk]&lt;br&gt;
   ↑                    ↑&lt;br&gt;
fruit points here    cart points here&lt;br&gt;
(len=3, cap=4)       (len=4, cap=4)&lt;/p&gt;

&lt;p&gt;After fruit = append(fruit, "lemon"):&lt;br&gt;
[Apple][Banana][Orange][lemon]&lt;br&gt;
   ↑                     ↑&lt;br&gt;
fruit points here     cart points here&lt;br&gt;
(len=4, cap=4)        (len=4, cap=4)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is This a Bug?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No, this is not a bug. This is intentional behavior designed for performance. Slices are designed to be lightweight views into arrays, avoiding unnecessary copying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memory Efficiency: Multiple slices can share the same underlying array&lt;/li&gt;
&lt;li&gt;Speed: No copying of data when creating sub-slices&lt;/li&gt;
&lt;li&gt;Flexibility: Easy to work with different "windows" of the same data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid Unexpected Behavior&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Use copy() for Independence&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fruit := make([]string, 3)&lt;br&gt;
copy(fruit, cart[:3])&lt;br&gt;
fruit = append(fruit, "lemon")&lt;br&gt;
// Now cart remains unchanged&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Force New Allocation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fruit := append([]string(nil), cart[:3]...)&lt;br&gt;
fruit = append(fruit, "lemon")&lt;br&gt;
// This creates a completely new slice&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use Full Slice Expression&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fruit := cart[:3:3]  // [low:high:max]&lt;br&gt;
fruit = append(fruit, "lemon")&lt;br&gt;
// This limits capacity, forcing new allocation&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slices are headers containing a pointer, length, and capacity&lt;/li&gt;
&lt;li&gt;Sub-slices share the same underlying array&lt;/li&gt;
&lt;li&gt;append() may modify the shared array if there's sufficient capacity&lt;/li&gt;
&lt;li&gt;This behavior is by design for performance, not a bug&lt;/li&gt;
&lt;li&gt;Use copy() or full slice expressions when you need independent slices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these internals will help you write more predictable Go code and avoid common pitfalls when working with slices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further Reading&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://go.dev/blog/slices-intro" rel="noopener noreferrer"&gt;Go Blog: Go Slices: usage and internals&lt;/a&gt;&lt;br&gt;
&lt;a href="https://go.dev/doc/effective_go#slices" rel="noopener noreferrer"&gt;Effective Go: Slices&lt;/a&gt;&lt;br&gt;
&lt;a href="https://go.dev/ref/spec#Slice_types" rel="noopener noreferrer"&gt;Go Specification: Slice types&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
