Fundamentals 9 min read

Go Language Fundamentals: Slices, Maps, Type Conversions, and Best Practices

This article introduces core Go concepts—including slice internals, common pitfalls when using slices and maps, type conversion techniques, function parameter behavior, and a stack‑overflow‑safe directory‑listing example—providing practical code snippets and guidance for reliable Go development.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Go Language Fundamentals: Slices, Maps, Type Conversions, and Best Practices

Go is an open‑source language that makes building simple, reliable, and efficient software easy; this tutorial walks through several fundamental topics.

Important Data Structure – Slice

A slice is Go's dynamic array. Its internal representation can be expressed as:

type slice struct {
    ptr *array // underlying array
    len int    // number of elements
    cap int    // capacity of the underlying array
}

Creating a slice:

s := make([]int, 5)

Basic slicing and indexing examples:

s := []int{1, 2, 3}
ss := s[1:3]
ss[0] = 0
fmt.Println(s, ss) // [1 2 3] [2 3]
s[1] = 11
fmt.Println(s, ss) // [1 11 3] [11 3]

Appending creates a new underlying array when needed:

s := []int{1, 2, 3}
ss := s[1:3]
ss = append(ss, 4)
fmt.Println(s, ss) // [1 2 3] [2 3 4]

Usage Notes – Common Slice Pitfalls

Multiple slices sharing the same backing array cause modifications in one slice to affect the others. The examples above demonstrate this behavior.

Map Basics and Concurrency

Maps must be initialized before use; otherwise a runtime panic occurs:

var m map[string]int
m["a"] = 1 // panic: assignment to entry in nil map

Correct initialization:

m := make(map[string]int)
m["a"] = 1
fmt.Println(m) // map[a:1]

When a map is passed to a function, modifications affect the original map:

func main() {
    m := make(map[string]int)
    m["a"] = 1
    fmt.Println(m) // map[a:1]
    foo(m)
    fmt.Println(m) // map[a:11]
}

func foo(fm map[string]int) {
    fm["a"] = 11
}

Concurrent reads/writes to a map without synchronization cause a panic; protect map access with a mutex.

Type Conversion

Two main forms:

Simple conversion within the same family (e.g., int to int64): var a int = 1 b := int64(a)

Conversion between unrelated types using the strconv package or type assertions for interfaces.

Interface to concrete type via assertion:

var a interface{} = 1
b := a.(int) // succeeds

Failed assertions panic; recover can be used to handle the panic:

defer func() {
    if r := recover(); r != nil {
        fmt.Println(r)
    }
}()
var a interface{} = 1
b := a.(string) // panic: interface conversion: interface {} is int, not string

Function Parameters – Value vs. Pointer

Struct arguments are passed by value, so modifications inside the function do not affect the caller:

type ta struct { i int }

func main() {
    var a ta
    a.i = 1
    foo(a)
    fmt.Println(a) // {1}
}

func foo(t ta) { t.i = 11 }

Methods with a value receiver behave the same; use a pointer receiver to modify the original value.

Preventing Stack Overflow

Recursive functions can overflow the stack for deep inputs; converting recursion to an iterative approach using slices (heap‑allocated) avoids this. Example: a non‑recursive directory walker that lists all files:

func ListFilesInDir(rootDir string) ([]string, error) {
    rootDir = strings.TrimRight(rootDir, "/")
    if !DirExist(rootDir) {
        return nil, errors.New("Dir not exists")
    }
    var fileList []string
    dirList := []string{rootDir}
    for i := 0; i < len(dirList); i++ {
        curDir := dirList[i]
        file, err := os.Open(curDir)
        if err != nil { return nil, err }
        fis, err := file.Readdir(-1)
        if err != nil { return nil, err }
        for _, fi := range fis {
            path := curDir + "/" + fi.Name()
            if fi.IsDir() {
                dirList = append(dirList, path)
            } else {
                fileList = append(fileList, path)
            }
        }
    }
    return fileList, nil
}

This iterative solution leverages slices to store pending directories, keeping memory on the heap and eliminating deep recursion.

References

Go Slices: usage and internals – https://blog.golang.org/go-slices-usage-and-internals

ConcurrencyGorecursionType ConversionSlicesmaps
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.