Neural Architecture Hub
Go

Go

Go language syntax, tooling, modules, concurrency, testing, and standard library reference.

Go Developer Guide


Setting Up Go

Install & Verify

# Download from https://golang.org/dl/
go version                        # Check installed Go version
go env                            # Show all Go environment variables
go env GOPATH                     # Show GOPATH directory
go env GOROOT                     # Show Go installation directory

Initialize a Project

go mod init <module_name>         # Initialize a new module (e.g. go mod init myapp)
go mod tidy                       # Add missing and remove unused dependencies
go mod download                   # Download all dependencies to local cache
go mod vendor                     # Copy dependencies into ./vendor directory

Running & Building

go run main.go                    # Compile and run a Go file directly
go run .                          # Run the package in current directory
go build                          # Compile binary into current directory
go build -o myapp                 # Compile with custom output name
go build ./...                    # Build all packages in module
go install                        # Compile and install to $GOPATH/bin
go clean                          # Remove compiled object files

Basic Syntax

Hello World

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Variables & Constants

// Variable declaration
var name string = "Go"            // Explicit type declaration
var age int                       // Zero value: 0
message := "Hello"                // Short declaration (inside functions only)

// Multiple variables
var x, y int = 10, 20
a, b := true, 3.14

// Constants
const Pi = 3.14159
const (
    StatusOK    = 200
    StatusError = 500
)

Data Types

// Basic types
bool                              // true / false
string                            // "text" (UTF-8)
int  int8  int16  int32  int64    // Signed integers
uint uint8 uint16 uint32 uint64   // Unsigned integers
float32 float64                   // Floating point
complex64 complex128              // Complex numbers
byte                              // Alias for uint8
rune                              // Alias for int32 (Unicode code point)

// Type conversion (explicit only — no implicit casting)
i := 42
f := float64(i)
u := uint(f)
s := string(rune(65))             // "A"

Control Flow

If / Else

if x > 10 {
    fmt.Println("big")
} else if x > 5 {
    fmt.Println("medium")
} else {
    fmt.Println("small")
}

// If with init statement
if val, err := strconv.Atoi("42"); err == nil {
    fmt.Println(val)
}

Switch

switch day {
case "Mon", "Tue", "Wed", "Thu", "Fri":
    fmt.Println("Weekday")
case "Sat", "Sun":
    fmt.Println("Weekend")
default:
    fmt.Println("Unknown")
}

// Typeless switch (replaces long if-else)
switch {
case age < 18:
    fmt.Println("Minor")
case age >= 18:
    fmt.Println("Adult")
}

Loops

// For (Go's only loop keyword)
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// While-style
for x < 100 {
    x *= 2
}

// Infinite loop
for {
    // break to exit
}

// Range over slice/array
for i, v := range nums {
    fmt.Println(i, v)
}

// Range over map
for k, v := range myMap {
    fmt.Println(k, v)
}

// Range over string (yields runes)
for i, ch := range "hello" {
    fmt.Printf("%d: %c\n", i, ch)
}

Functions

Basic Functions

func add(a, b int) int {
    return a + b
}

// Multiple return values
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// Named return values
func minMax(nums []int) (min, max int) {
    min, max = nums[0], nums[0]
    for _, v := range nums {
        if v < min { min = v }
        if v > max { max = v }
    }
    return                        // Naked return
}

// Variadic function
func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}
sum(1, 2, 3)                      // Pass individual args
sum(nums...)                      // Spread a slice

First-Class Functions

// Function as variable
double := func(n int) int { return n * 2 }
fmt.Println(double(5))            // 10

// Function as argument
apply := func(f func(int) int, x int) int { return f(x) }

// Immediately invoked function
result := func(x int) int { return x * x }(9)

// Closure
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
c := counter()
c() // 1
c() // 2

Defer, Panic, Recover

// Defer: runs when surrounding function returns (LIFO order)
defer fmt.Println("cleanup")

// Panic: stops normal execution
panic("something went wrong")

// Recover: catches a panic (must be inside defer)
defer func() {
    if r := recover(); r != nil {
        fmt.Println("Recovered:", r)
    }
}()

Arrays, Slices & Maps

Arrays

var arr [3]int                    // [0, 0, 0]
arr := [3]int{1, 2, 3}
arr := [...]int{4, 5, 6}         // Compiler infers length
arr[0] = 10
len(arr)                          // 3

Slices

s := []int{1, 2, 3}              // Slice literal
s := make([]int, 5)              // Length 5, capacity 5
s := make([]int, 3, 10)          // Length 3, capacity 10

s = append(s, 4)                 // Append single element
s = append(s, 5, 6, 7)          // Append multiple
s = append(s, other...)          // Append another slice

s[1:3]                           // Slice of index 1 to 2 (not 3)
s[:3]                            // From start to index 2
s[2:]                            // From index 2 to end
copy(dst, src)                   // Copy elements from src to dst

len(s)                           // Number of elements
cap(s)                           // Underlying array capacity

Maps

m := map[string]int{"a": 1, "b": 2}
m := make(map[string]int)

m["key"] = 42                    // Set value
v := m["key"]                    // Get value (zero if missing)
v, ok := m["key"]                // ok is false if key absent
delete(m, "key")                 // Remove key

// Iterate
for k, v := range m {
    fmt.Println(k, v)
}

Structs

Definition & Usage

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}  // Named fields
p := Person{"Alice", 30}             // Positional (fragile, avoid)
p.Name = "Bob"                        // Field access
pp := &Person{Name: "Carol"}         // Pointer to struct

// Anonymous struct
config := struct {
    Host string
    Port int
}{"localhost", 8080}

Methods

// Value receiver (copy)
func (p Person) Greet() string {
    return "Hi, I'm " + p.Name
}

// Pointer receiver (modifies original)
func (p *Person) Birthday() {
    p.Age++
}

p.Birthday()                     // Auto-dereferenced
(&p).Birthday()                  // Equivalent

Embedding (Composition)

type Animal struct {
    Name string
}
func (a Animal) Speak() string { return a.Name + " speaks" }

type Dog struct {
    Animal                        // Embedded — promotes fields/methods
    Breed string
}

d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Lab"}
d.Speak()                        // Promoted from Animal
d.Name                           // Promoted field

Interfaces

Definition & Implementation

type Shape interface {
    Area() float64
    Perimeter() float64
}

type Rectangle struct{ Width, Height float64 }

func (r Rectangle) Area() float64      { return r.Width * r.Height }
func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) }

// Implicit satisfaction — no "implements" keyword
var s Shape = Rectangle{5, 3}
s.Area()                         // 15

Empty Interface & Type Assertions

var i interface{} = "hello"       // any type (use `any` in Go 1.18+)
var a any = 42

// Type assertion
s := i.(string)                   // Panics if wrong type
s, ok := i.(string)               // Safe form

// Type switch
switch v := i.(type) {
case int:    fmt.Println("int:", v)
case string: fmt.Println("string:", v)
default:     fmt.Println("other")
}

Pointers

x := 42
p := &x                          // & = address of x
*p = 100                         // * = dereference (modifies x)
fmt.Println(x)                   // 100

// new() allocates zeroed memory, returns pointer
p := new(int)
*p = 7

// Pointers with structs
type Node struct{ Val int }
n := &Node{Val: 1}
n.Val = 2                        // Auto-dereferenced (no n->Val like C)

Error Handling

// Idiomatic: return error as last value
func readFile(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("readFile: %w", err)  // Wrap error
    }
    return data, nil
}

// Check and handle
data, err := readFile("config.json")
if err != nil {
    log.Fatal(err)
}

// Custom error types
type ValidationError struct {
    Field   string
    Message string
}
func (e *ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Message)
}

// Unwrap errors (Go 1.13+)
errors.Is(err, os.ErrNotExist)    // Check error chain
errors.As(err, &myErr)            // Extract concrete type from chain

Goroutines & Concurrency

Goroutines

go func() {
    fmt.Println("runs concurrently")
}()

go doWork(arg)                    // Launch any function as goroutine

// Wait for goroutines with WaitGroup
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        fmt.Println("worker", id)
    }(i)
}
wg.Wait()                        // Block until all Done()

Channels

ch := make(chan int)              // Unbuffered channel
ch := make(chan int, 10)          // Buffered channel (capacity 10)

ch <- 42                         // Send (blocks if unbuffered/full)
v := <-ch                        // Receive (blocks until data)
v, ok := <-ch                    // ok is false if channel closed
close(ch)                        // Signal no more sends

// Range over channel
for v := range ch {              // Exits when channel closed
    fmt.Println(v)
}

// Directional channels (for function params)
func producer(out chan<- int) { out <- 1 }   // Send-only
func consumer(in <-chan int)  { fmt.Println(<-in) }  // Receive-only

Select

select {
case msg := <-ch1:
    fmt.Println("ch1:", msg)
case msg := <-ch2:
    fmt.Println("ch2:", msg)
case ch3 <- "hello":
    fmt.Println("sent to ch3")
default:
    fmt.Println("no channel ready")  // Non-blocking
}

Mutex & Sync Primitives

var mu sync.Mutex
mu.Lock()
// critical section
mu.Unlock()

// RWMutex: multiple readers, one writer
var rw sync.RWMutex
rw.RLock()   / rw.RUnlock()      // Shared read lock
rw.Lock()    / rw.Unlock()       // Exclusive write lock

// Once: run exactly once
var once sync.Once
once.Do(func() { fmt.Println("only once") })

// Atomic operations
var counter int64
atomic.AddInt64(&counter, 1)
atomic.LoadInt64(&counter)

Generics (Go 1.18+)

// Generic function
func Map[T, U any](s []T, f func(T) U) []U {
    result := make([]U, len(s))
    for i, v := range s {
        result[i] = f(v)
    }
    return result
}

// Type constraints
type Number interface {
    int | int64 | float64
}

func Sum[T Number](nums []T) T {
    var total T
    for _, n := range nums {
        total += n
    }
    return total
}

// Generic struct
type Stack[T any] struct {
    items []T
}
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
func (s *Stack[T]) Pop() T {
    n := len(s.items) - 1
    v := s.items[n]
    s.items = s.items[:n]
    return v
}

Packages & Modules

go get github.com/pkg/errors          # Add a dependency
go get github.com/pkg/errors@v1.9.1  # Specific version
go get github.com/pkg/errors@latest  # Latest version
go list -m all                        # List all module dependencies
go mod graph                          # Show dependency graph
go mod why github.com/pkg/errors      # Explain why dep is needed
// Import styles
import "fmt"                          // Standard library
import "github.com/pkg/errors"        // Third-party
import (                              // Grouped imports
    "fmt"
    "os"
    "github.com/pkg/errors"
)
import _ "image/png"                  // Blank import (side effects only)
import f "fmt"                        // Aliased import

File I/O

// Read entire file
data, err := os.ReadFile("file.txt")

// Write entire file
err := os.WriteFile("file.txt", []byte("hello"), 0644)

// Open / close
f, err := os.Open("file.txt")        // Read-only
defer f.Close()

f, err := os.Create("file.txt")      // Write (truncates)
f, err := os.OpenFile("file.txt", os.O_APPEND|os.O_WRONLY, 0644)

// Buffered reader
scanner := bufio.NewScanner(f)
for scanner.Scan() {
    fmt.Println(scanner.Text())       // Line by line
}

// Buffered writer
w := bufio.NewWriter(f)
fmt.Fprintln(w, "line")
w.Flush()                             // Don't forget to flush!

Testing

go test ./...                         # Run all tests in module
go test -v ./...                      # Verbose output
go test -run TestFuncName             # Run specific test
go test -bench=.                      # Run benchmarks
go test -cover                        # Show coverage summary
go test -coverprofile=cov.out         # Write coverage profile
go tool cover -html=cov.out           # Open coverage in browser
go test -race ./...                   # Run with race detector
// Basic test (file must end in _test.go)
func TestAdd(t *testing.T) {
    got := add(2, 3)
    if got != 5 {
        t.Errorf("add(2,3) = %d; want 5", got)
    }
}

// Table-driven tests
func TestMultiply(t *testing.T) {
    tests := []struct {
        a, b, want int
    }{
        {2, 3, 6},
        {0, 5, 0},
        {-1, 4, -4},
    }
    for _, tc := range tests {
        got := multiply(tc.a, tc.b)
        if got != tc.want {
            t.Errorf("multiply(%d,%d) = %d; want %d", tc.a, tc.b, got, tc.want)
        }
    }
}

// Benchmark
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        add(1, 2)
    }
}

// Subtests
func TestGroup(t *testing.T) {
    t.Run("case A", func(t *testing.T) { /* ... */ })
    t.Run("case B", func(t *testing.T) { /* ... */ })
}

Common Standard Library Packages

fmt

fmt.Println("hello", x)               # Print with newline
fmt.Printf("name: %s, age: %d\n", n, a)  # Formatted print
fmt.Sprintf("val: %v", x)             # Format to string
fmt.Fprintf(w, "to writer: %s", s)    # Format to io.Writer
fmt.Errorf("wrap: %w", err)           # Create formatted error
// Verbs: %v (default), %T (type), %d (int), %f (float), %s (string), %q (quoted), %p (pointer)

strings

strings.Contains(s, "sub")            # true/false
strings.HasPrefix(s, "pre")           # true/false
strings.HasSuffix(s, "suf")           # true/false
strings.ToUpper(s) / strings.ToLower(s)
strings.TrimSpace(s)                  # Remove leading/trailing whitespace
strings.Trim(s, "!?")                 # Remove specific chars from both ends
strings.Split(s, ",")                 # []string
strings.Join(parts, "-")              # string
strings.Replace(s, "old", "new", n)   # n=-1 replaces all
strings.Count(s, "sub")               # Occurrences
strings.Index(s, "sub")               # First index, -1 if not found
strings.TrimPrefix(s, "prefix")
strings.Fields(s)                     # Split on any whitespace

strconv

strconv.Atoi("42")                    # stringint (returns int, error)
strconv.Itoa(42)                      # intstring
strconv.ParseFloat("3.14", 64)        # stringfloat64
strconv.FormatFloat(3.14, 'f', 2, 64) # float64string "3.14"
strconv.ParseBool("true")             # stringbool
strconv.FormatBool(true)              # boolstring

os & path

os.Getenv("HOME")                     # Get environment variable
os.Setenv("KEY", "val")               # Set environment variable
os.Args                               # []string of CLI arguments
os.Exit(1)                            # Exit with code
os.Mkdir("dir", 0755)                 # Create directory
os.MkdirAll("a/b/c", 0755)           # Create nested directories
os.Remove("file.txt")                 # Delete file
os.Rename("old", "new")               # Move/rename
os.Stat("file.txt")                   # Get file info (FileInfo, error)

filepath.Join("a", "b", "c")         # "a/b/c" (OS-safe)
filepath.Dir("/a/b/c")                # "/a/b"
filepath.Base("/a/b/c")               # "c"
filepath.Ext("file.txt")              # ".txt"

time

time.Now()                            # Current time
time.Now().Unix()                     # Unix timestamp (seconds)
time.Sleep(2 * time.Second)           # Sleep for duration
time.Second / time.Millisecond / time.Minute

t := time.Now()
t.Format("2006-01-02 15:04:05")       // Format (Go's reference time)
time.Parse("2006-01-02", "2024-01-15") // Parse string to Time

since := time.Since(start)           // Duration elapsed
until := time.Until(deadline)        // Duration remaining

ticker := time.NewTicker(1 * time.Second)
timer  := time.NewTimer(5 * time.Second)

Context

// Create contexts
ctx := context.Background()           // Root context (never cancelled)
ctx := context.TODO()                 // Placeholder context

// With cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()                        // Always call cancel

// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// With deadline
deadline := time.Now().Add(10 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()

// With value
ctx = context.WithValue(ctx, "userID", 42)
val := ctx.Value("userID").(int)

// Check cancellation in goroutines
select {
case <-ctx.Done():
    fmt.Println("cancelled:", ctx.Err())  // context.Canceled or DeadlineExceeded
default:
    // continue work
}

HTTP Server & Client

HTTP Server

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
})
http.ListenAndServe(":8080", nil)

// With custom mux
mux := http.NewServeMux()
mux.HandleFunc("/api/users", usersHandler)
server := &http.Server{
    Addr:         ":8080",
    Handler:      mux,
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
}
server.ListenAndServe()

HTTP Client

// Simple GET
resp, err := http.Get("https://api.example.com/data")
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)

// With timeout
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get("https://api.example.com")

// POST with JSON
payload, _ := json.Marshal(map[string]string{"key": "val"})
resp, err := http.Post(url, "application/json", bytes.NewBuffer(payload))

// Custom request with headers
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, err := client.Do(req)

JSON Encoding & Decoding

type User struct {
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`   // Omit if empty
    pass  string                            // Unexported = ignored
}

// Struct → JSON
data, err := json.Marshal(u)
jsonStr := string(data)

// JSON → Struct
var u User
err := json.Unmarshal([]byte(jsonStr), &u)

// Stream encoding/decoding
enc := json.NewEncoder(w)       // w = io.Writer (e.g. ResponseWriter)
enc.Encode(u)

dec := json.NewDecoder(r.Body)  // r.Body = io.Reader
dec.Decode(&u)

// Arbitrary JSON
var raw map[string]interface{}
json.Unmarshal(data, &raw)

Useful Go CLI Tools

go fmt ./...                          # Format all Go source files
go vet ./...                          # Report likely mistakes in code
go doc fmt.Println                    # Show documentation for a symbol
go doc -all fmt                       # Show all docs for a package
godoc -http=:6060                     # Serve local documentation
go generate ./...                     # Run //go:generate directives
gopls                                 # Official Go language server (LSP)
staticcheck ./...                     # Advanced static analysis (install separately)
dlv debug main.go                     # Debug with Delve debugger

Go Proverbs (Best Practices)

Don't communicate by sharing memory; share memory by communicating.
Concurrency is not parallelism.
Errors are values — handle them, don't ignore them.
The bigger the interface, the weaker the abstraction.
Accept interfaces, return structs.
Make the zero value useful.
A little copying is better than a little dependency.
Clear is better than clever.

On this page