Skip to content
gohacks.dev
Basics

Go Builtin Functions You Should Know Cold Before Coding

All eighteen Go builtin functions in one table, grouped by purpose, with real output and the traps in len, copy, delete, min and print.

6 min read
Summarize in AI
Flat illustration of an editor window full of code.

The builtin functions are the part of Go's vocabulary that you never import and rarely read about, because each one is obvious until the day it is not. They are defined by the language and not by any package, which is why several of them do things ordinary functions cannot: len works on strings, arrays, slices, maps and channels, make takes a type as its first argument and append changes its result type with its input. This reference lists all of them, runs each against real values, and notes the corner that bites. It is part of the Go basics path, and each of the data structures has its own article.

Tested with Go 1.25.6, macOS on Apple Silicon (arm64)

The list

BuiltinWhat it doesWorks on
append(s, x...)Returns a slice with the elements addedSlices (and []byte plus a string)
len(x)Number of elements or bytesString, array, slice, map, channel
cap(x)CapacityArray, slice, channel
make(T, n...)Allocates and initializesSlice, map, channel
new(T)Allocates a zeroed T, returns *TAny type
copy(dst, src)Copies elements, returns the countSlices, and a string source for []byte
delete(m, k)Removes a keyMaps
clear(x)Empties a map or zeroes a slice's elementsMaps, slices
close(ch)Marks a channel as having no more sendsChannels
min(x...), max(x...)Smallest or largest argumentOrdered types
complex(r, i), real(c), imag(c)Build and take apart complex numbersFloats and complex values
panic(v)Stops the normal flow and unwindsAny value
recover()Stops a panic, inside a deferred function
print(...), println(...)Low level output to standard errorBasic types

That is eighteen names counting min and max, real and imag, and print and println separately. Outside the list sit the predeclared constants and values, true, false, iota and nil, and the predeclared types such as int, string, error and any.

Running each

One program, with its output, exercises every row in the table:

main.goGo
package main

import (
	"fmt"
	"math"
)

func main() {
	s := []int{1, 2, 3}
	s = append(s, 4, 5)
	t := append([]byte("go"), " lang"...)
	fmt.Println(s, len(s), cap(s) >= 5, string(t))

	arr := [4]int{}
	m := map[string]int{"a": 1}
	ch := make(chan int, 3)
	ch <- 1
	fmt.Println(len("héllo"), len(arr), cap(arr), len(m), len(ch), cap(ch))

	a := make([]int, 2, 10)
	b := make(map[string]bool, 100)
	c := make(chan struct{})
	fmt.Println(len(a), cap(a), len(b), c != nil)

	p := new(int)
	*p = 7
	fmt.Println(*p)

	dst := make([]int, 2)
	n := copy(dst, s)
	bs := make([]byte, 3)
	n2 := copy(bs, "hello")
	fmt.Println(n, dst, n2, string(bs))

	delete(m, "a")
	delete(m, "missing")
	fmt.Println(len(m))

	clear(s)
	fmt.Println(s)

	fmt.Println(min(3, 1, 2), max(2.5, 1), min("b", "a"), max(1, 2.5))
	fmt.Println(min(1, math.NaN()), max(math.Inf(-1), 0))

	z := complex(3, 4)
	fmt.Println(real(z), imag(z), z*z)

	close(ch)
	v, ok := <-ch
	fmt.Println(v, ok)
	v, ok = <-ch
	fmt.Println(v, ok)

	defer func() {
		fmt.Println("recovered:", recover())
	}()
	panic("boom")
}
Text
[1 2 3 4 5] 5 true go lang
6 4 4 1 1 3
2 10 0 true
7
2 [1 2] 3 hel
0
[0 0 0 0 0]
1 2.5 a 2.5
NaN 0
3 4 (-7+24i)
1 true
0 false
recovered: boom

Read the results against the table. After append, the slice has five elements, and a capacity of at least five, which is all the language promises. The byte slice append used the special form that accepts a string with .... len("héllo") is 6, counting bytes, as strings explain. An array's cap equals its length. A buffered channel reports the number of queued values with len and its buffer size with cap, so len(ch) was 1 after one send. make with a length and capacity gave a slice of length 2 and capacity 10, and the size argument to a map is a hint and not a limit.

The corners, builtin by builtin

append returns a new slice header, and ignoring the result is a compile error, append(s, 1) (value of type []int) is not used. It may or may not reuse the array, which the append trap explains in detail.

len and cap are evaluated at compile time for arrays and constant strings. For a nil slice, map or channel, len is zero.

make is for slices, maps and channels only. make([]int, n) creates n zero elements, which is not what you want before appending, as slices explained warns. new(T) returns a pointer to a zero T, and &T{} is more common for structs, as pointers in Go notes.

copy copies as many elements as the shorter of the two slices, 3 bytes here from "hello". It handles overlapping ranges correctly, so you can shift elements inside one slice, and it accepts a string source only for a byte destination. The destination must already have length, as copy a slice the safe way shows.

delete on a missing key or on a nil map is a no-op and does not panic.

clear sets every map entry to nothing for a map and every element to its zero value for a slice, without changing the slice's length. It arrived in Go 1.21.

min and max, also Go 1.21, take two or more values of one ordered type, and untyped constants mix as in max(1, 2.5), which printed 2.5. If any argument is a floating NaN, min and max return NaN, as the output NaN shows. For strings they compare bytes. They replace the hand-written if and the math.Min calls, which always use float64.

close is for channels, and the receive after close returns the zero value and false, the "comma ok" form that the output shows as 0 false. Sending on a closed channel panics, and closing a nil or already closed channel panics. The rules are in closing channels and who may do it.

complex, real, imag are rarely used outside numeric code. The result of complex(3, 4) squared is (-7+24i).

panic and recover are the exception mechanism, covered in recovering from panics. recover only works inside a deferred function, and returns nil otherwise.

print and println write to standard error, with their own formatting, not fmt's, and the specification says they are not guaranteed to stay. Use them to debug the runtime or in a program with no imports, and use fmt or log for anything else. A println of a float prints in a scientific format that is surprising the first time.

What is not on the list

Familiar functions that are not builtins: fmt.Println, strings.Contains, sort.Ints, os.Exit. Anything you have to import is a library function. The standard math package has math.Max and math.Min, but they work on float64 only, and min and max are the better choice for new code. Operations that look like builtins, such as len on a string, are language features and cannot be passed as function values. To use len as a callback, wrap it in a function literal. The authoritative definitions are in the specification's list of built-in functions, and the same section of the Go 1.21 release notes describes the additions.

Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.