Essential Built-in Functions
Go includes a set of fundamental built-in functions that every developer should know:
close– shuts down a channellen– returns the length of a string, array, slice, map, or channelnew– allocates memory for a value type (e.g.,int,struct) and returns a pointermake– allocates and initializes a reference type (slice, map, channel)append– adds elements to a slice or arraypanicandrecover– used for error‑handling flow control
The difference between new and make
new(T) returns a *T pointing to a zero‑initialized value; you often need further initialization. make(T, ...) returns an initialized T itself, ready to use.
package main
import "fmt"
func main() {
// new returns a pointer to an allocated (but nil) slice header
ptr := new([]int)
fmt.Println(*ptr) // []
*ptr = make([]int, 4) // initialize the slice with length 4
(*ptr)[0] = 25
fmt.Println(*ptr) // [25 0 0 0]
// make directly returns a usable slice
nums := make([]int, 4)
nums[0] = 17
fmt.Println(nums) // [17 0 0 0]
}
Using append
package main
import "fmt"
func main() {
var data []int
data = append(data, 5, 12, 97, 33)
data = append(data, data...) // duplicate the entire slice
fmt.Println(data)
}
panic and recover
panic generates a runtime error, while recover can catch it inside a deferred function.
package main
import (
"errors"
"fmt"
"time"
)
func readConfig() error {
return errors.New("config missing")
}
func attempt() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}()
if err := readConfig(); err != nil {
panic(err)
}
}
func main() {
for {
attempt()
time.Sleep(time.Second)
}
}
Recursive Functions
Design principles: a large problem can be divided into smaller identical subproblems; a clear termination condition must be defined.
package main
import "fmt"
func countDown(n int) {
if n < 0 {
return
}
fmt.Println("tick", n)
countDown(n - 1)
}
func factorial(x int) int {
if x == 0 {
return 1
}
return x * factorial(x-1)
}
func fib(n int) int {
if n <= 1 {
return 1
}
return fib(n-1) + fib(n-2)
}
func main() {
fmt.Println("5! =", factorial(5))
countDown(2)
for i := 0; i < 5; i++ {
fmt.Printf("Fib(%d) = %d\n", i, fib(i))
}
}
Closures
A closure is a function value that references variables from outside its body. The funcsion together with those referenced variables forms the closure.
Closure without parameters
package main
import "fmt"
func accumulator() func(int) int {
var sum int
return func(delta int) int {
sum += delta
return sum
}
}
func main() {
a := accumulator()
fmt.Println(a(1)) // 1
fmt.Println(a(100)) // 101
fmt.Println(a(1000)) // 1101
}
Closure with a parameter
package main
import (
"fmt"
"strings"
)
func makeExtAdder(ext string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, ext) {
return name + ext
}
return name
}
}
func main() {
addBmp := makeExtAdder(".bmp")
fmt.Println(addBmp("photo")) // photo.bmp
fmt.Println(addBmp("icon.bmp")) // icon.bmp
addJpg := makeExtAdder(".jpg")
fmt.Println(addJpg("img")) // img.jpg
}
Arrays
An array is a fixed‑length sequence of zero or more elements of the same type. The length is part of the array’s type, so [3]int and [4]int are distinct types. Elements are accessed by zero‑based index.
Declaration and Initialization
var arr1 [3]int // all zeros
arr2 := [5]int{10, 20, 30, 40, 50}
arr3 := [...]int{1, 2, 3} // length inferred as 3
arr4 := [4]string{1: "Go", 3: "Rust"}
Arrays are value types. Passing an array to a function copies the entire array; modifications inside the function do not affect the original.
package main
import "fmt"
func modifyCopy(a [3]int) {
a[0] = 999
fmt.Println("inside:", a)
}
func main() {
original := [3]int{1, 2, 3}
modifyCopy(original)
fmt.Println("outside:", original) // remains [1 2 3]
}
To modify the original array, pass a pointer.
func modifyViaPointer(a *[3]int) {
a[0] = 888
}
func main() {
arr := [3]int{1, 2, 3}
modifyViaPointer(&arr)
fmt.Println(arr) // [888 2 3]
}
Iteration
nums := [3]int{7, 8, 9}
// index-only
for i := 0; i < len(nums); i++ {
fmt.Println(nums[i])
}
// index and value
for idx, val := range nums {
fmt.Printf("%d -> %d\n", idx, val)
}
Array Comparison
Arrays of the same element type can be compared with == and !=. Arrays of different lengths cannot be compared (compile‑time error).
a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{3, 2}
d := [3]int{1, 2, 3}
fmt.Println(a == b, a == c, b == c) // true false false
// fmt.Println(a == d) // compilation error
Slices
A slice is a flexible, dynamically‑sized view into a contiguous segment of an array. A slice has three components: a pointer to the underlying array’s element, a length (number of accessible elements), and a capacity (maximum length starting from the pointer).
Creating Slices
var arr [5]int = [5]int{10, 20, 30, 40, 50}
s1 := arr[1:4] // [20 30 40], len=3, cap=4
s2 := arr[2:] // [30 40 50], len=3, cap=3
s3 := arr[:3] // [10 20 30], len=3, cap=5
s4 := []int{5, 6, 7} // literal slice
Slices are reference types—modifying a slice’s elements affects the underlying array.
func updateSlice(s []int) {
s[0] = 100
}
func main() {
base := [3]int{1, 2, 3}
sl := base[1:] // [2 3]
updateSlice(sl)
fmt.Println(base) // [1 100 3]
}
The make function creates a slice with a specified length and optional capacity, allocating an anonymous underlying array.
s := make([]int, 3, 5) // len=3, cap=5
fmt.Println(s, len(s), cap(s)) // [0 0 0] 3 5
append and Capacity Growth
append adds elements. If the capacity is exceeded, a new underlying array is allocated, often doubling the capacity, and the original elements are copied.
nums := []int{1, 2, 3}
fmt.Println(len(nums), cap(nums)) // 3 3
nums = append(nums, 4)
fmt.Println(len(nums), cap(nums)) // 4 6 (capacity doubled)
nums = append(nums, 5, 6, 7)
fmt.Println(len(nums), cap(nums)) // 7 12
copy Function
copy(dst, src) copies elements from src to dst. Only the minimum of the two lengths is copied.
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 2)
copied := copy(dst, src)
fmt.Println(dst, copied) // [1 2] 2
Removing an Element from a Slice
data := []int{10, 15, 8, 20}
i := 2 // remove element at index 2
data = append(data[:i], data[i+1:]...)
fmt.Println(data) // [10 15 20]
Strings, Runes, and Bytes
Go strings are immutable sequences of bytes. When iterating with range, each value is a rune (an alias for int32 representing a Unicode code point).
text := "Hello, 世界"
fmt.Println(len(text)) // 13 (bytes)
fmt.Println(len([]rune(text))) // 9 (characters)
for _, r := range text {
fmt.Printf("%c ", r)
}
Modifying a String
Becuase strings are immutable, you must convert to a rune or byte slice, modify, and then convert back.
package main
import "fmt"
func toLowercase(s string) {
runes := []rune(s)
runes[0] = 'h' // change first rune
result := string(runes)
fmt.Println(result) // "hello, 世界"
}
func main() {
toLowercase("Hello, 世界")
}
Reversing a String
func reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
Time and Date
now := time.Now()
fmt.Println(now.Year(), now.Month(), now.Day())
fmt.Println(now.Hour(), now.Minute(), now.Second())
time.Duration represents nanoseconds. Useful constants: time.Nanosecond, time.Microsecond, time.Millisecond, time.Second, time.Minute, time.Hour.
Formatting a time requires the reference layout "2006-01-02 15:04:05" (Go’s birth date).
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))