Operators in Go seem trivial, but their interactions with types can silently trigger allocations. Let’s dissect arithmetic, comparison, and bitwise operators alongside type conversions to expose their memory impact.


The Code (Now with Explicit Operators)

package main  

func main() {  
    // CASE 1: Arithmetic operators with mixed types  
    a := 100              // int (stack)  
    b := int32(200)       // int32 (stack)  
    sum := a + int(b)     // int(b) forces conversion, sum on stack  

    // CASE 2: String concatenation (operator +)  
    s1 := "Hello"         // data segment  
    s2 := "Go"            // data segment  
    result := s1 + " " + s2  // New string allocation in data  

    // CASE 3: Interface comparison (operator ==)  
    var v1 interface{} = sum  // Heap escape  
    var v2 interface{} = 300  // Heap escape  
    if v1 == v2 {             // Compares type and value  
        // ...  
    }  
}

Memory Segmentation Visualized

+-------------------+   Lowest Address  
|      Code         |  
|-------------------|  
| main()            |  // Arithmetic, comparison logic  
| string concat     |  // Runtime functions for +  
+-------------------+  
|      Data         |  
|-------------------|  
| "Hello"           |  
| "Go"              |  
| "Hello Go"        |  // result's new string  
+-------------------+  
|      Stack        |  
|-------------------|  
| a (int = 100)     |  
| b (int32 = 200)   |  
| sum (int = 300)   |  
| s1, s2 (strings)  |  // Pointers to data  
| result (string) → |  // Points to new data segment  
+-------------------+  
|      Heap         |  
|-------------------|  
| v1 (eface)        |  // Boxed int (300)  
| v2 (eface)        |  // Boxed int (300)  
+-------------------+   Highest Address

Operator Breakdown

1. Arithmetic Operators (+, -, *, /)

  • Mixed types: a + int(b) forces an int32int conversion.
    • Cost: Stack-only operation (no allocation).
  • Division: / on integers is stack-resident but may panic (e.g., division by zero).

2. String Concatenation (+)

  • Allocation: s1 + " " + s2 creates a new string in the data segment.
    • Hidden cost: O(n) time and space (avoids mutation).
  • Verify:
go build -gcflags="-m" main.go  
  # ./main.go:9:18: " " escapes to heap  # Part of new string allocation

3. Comparison Operator (==)

  • Interfaces: v1 == v2 compares both type and value.

    • Heap cost: v1 and v2 box integers into interface{}, forcing heap allocations.
    • Alternative: Compare primitives directly to avoid boxing:
    if sum == 300 { /* No allocation */ }  
    

Type Conversion Nuances

  1. Numeric Conversions
x := 3.14  
   y := int(x)    // Stack: truncates float to int (no allocation)
  1. String ↔ Slice
s := "Go"  
   b := []byte(s)  // Heap: allocates new []byte backing array
  • Data segment: s remains immutable; b is a heap-allocated copy.

Why This Matters

  • String + in loops: Can bloat the data segment (e.g., building HTML/CSS).
    • Fix: Use strings.Builder or bytes.Buffer.
  • Interface comparisons: Useful but heap-heavy. Avoid in hot paths.
  • Mixed-type arithmetic: May cause silent overflows (e.g., int32(bigInt)).

Optimization Tips

  1. Prefer switch type assertions over interface{} comparisons:
switch v := v1.(type) {  
   case int:  
       if v == 300 { ... }  // No allocation  
   }
  1. Avoid + for large strings:
var builder strings.Builder  
   builder.WriteString("Hello")  // Heap buffer, but amortized growth  
   builder.WriteString("Go")  
   result := builder.String()    // Single data segment allocation