Interview Question: Explain Slices in Golang

Publish date: 2025-01-18
Tags: Go, Interview-Questions

Slices in Go are essential for handling collections of data flexibly and efficiently. This guide will help you grasp the core concepts of slices, ensuring you are well-prepared for your upcoming Golang interview.

What Are Slices?

Slices are built on top of arrays and offer a dynamic way to manage sequences of elements. They consist of three main components:

package main

import "fmt"

func main() {
	var a = []int{10, 20, 30, 40, 50}
	b := a[:3]
	fmt.Println(b) // [10 20 30]
	fmt.Println(len(b)) // 3
	fmt.Println(cap(b)) // 5
}
Underlying Array: [10, 20, 30, 40, 50]
Slice Header: {
  Pointer: -> [10, 20, 30, 40, 50],
  Length: 3,
  Capacity: 5
}

Explain Slices in Golang

Key Properties of Slices

  1. Dynamic Lengths: Unlike arrays, slices can grow or shrink during runtime.
  2. Shared Underlying Arrays: Multiple slices can reference the same array; changes in one slice affect others.
  3. Value Semantics: Assigning a slice copies its header but not its underlying array.

Differences Between Slices and Arrays

Slice Header Structure

Internally, a slice is represented by a struct:

package runtime

type slice struct {
    ptr   unsafe.Pointer
    len   int
    cap   int
}

When passed to functions, only the slice header is copied; the underlying array remains unchanged.

Common Operations on Slices

  1. Appending Elements: Use the append function to add elements dynamically.

    slice := []int{1, 2, 3}
    slice = append(slice, 4, 5)
    fmt.Println(slice) // Output: [1, 2, 3, 4, 5]
    
  2. Copying Slices: Use copy to transfer elements between slices.

    src := []int{1, 2, 3}
    dst := make([]int, len(src))
    copy(dst, src)
    fmt.Println(dst) // Output: [1, 2, 3]
    
  3. Using Slices as Stacks: Implement stack operations using slices.

    Push operation:

    stack := []int{}
    stack = append(stack, 10) // Push
    

    Pop operation:

    val := stack[len(stack)-1] // Peek
    stack = stack[:len(stack)-1] // Pop
    
  4. Modifying Slices in Functions: Changes within functions affect the original slice due to shared underlying arrays.

    func negate(s []int) {
        for i := range s {
            s[i] = -s[i]
        }
    }
    
    func main() {
        a := []int{1, 2, 3}
        negate(a)
        fmt.Println(a) // Output: [-1 -2 -3]
    }
    
  5. Inserting Elements: Shift elements to insert new values.

    func Insert(slice []int, index, value int) []int {
        slice = append(slice, 0) // Extend length
        copy(slice[index+1:], slice[index:])
        slice[index] = value
        return slice
    }
    
    slice := []int{1, 2, 3}
    slice = Insert(slice, 1, 99)
    fmt.Println(slice) // Output: [1, 99, 2]
    

Nil vs. Empty Slices

var nilSlice []int
emptySlice := make([]int, 0)
fmt.Println(nilSlice == nil) // true
fmt.Println(emptySlice == nil) // false

Conclusion

Understanding slices is crucial for efficient coding in Go. Familiarize yourself with their properties and operations to excel in your Golang interviews. Mastering these concepts will enhance your ability to write effective and maintainable code in Go.

Citations:

Tags: Go, Interview-Questions