beginner languages Go 1.22 · Updated April 2026

Go Language Basics Cheatsheet

Master the fundamentals of Go (Golang) for rapid development, covering syntax, core concepts, and essential commands for version 1.22.

· 8 min read · AI-reviewed

Quick Overview

Go, often referred to as Golang, is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. It emphasizes simplicity, reliability, and efficiency, making it ideal for building scalable network services, command-line tools, and cloud infrastructure. Go includes built-in concurrency features and a robust standard library.

Current Stable Version: Go 1.22 Install: Download from go.dev/dl/ or use a package manager.

Getting Started

To get up and running with Go, you’ll first need to install the Go toolchain and then create your first program.

  1. Install Go 1.22: Download the appropriate installer for your operating system (Linux, macOS, Windows) from the official Go downloads page: go.dev/dl/. Follow the installation instructions provided on the Go website: go.dev/doc/install.

    • macOS (Homebrew):
      # Install Go 1.22 via Homebrew
      brew install [email protected]
    • Linux/Windows (manual download is often simplest): Download the .tar.gz (Linux/macOS) or .zip (Windows) archive, extract it to /usr/local (Linux/macOS) or C:\Go (Windows), and configure your PATH environment variable.
  2. Verify Installation: Open a new terminal or command prompt and run:

    # Check the Go version
    go version

    You should see output similar to go version go1.22.x <os>/<arch>.

  3. Your First Go Program (Hello World): Create a new directory for your project:

    # Create a project directory
    mkdir myapp
    cd myapp

    Initialize a Go module (this manages dependencies):

    # Initialize a Go module
    go mod init myapp

    Create a file named main.go with the following content:

    // main.go
    package main // Declares the package as 'main', making it an executable program
    
    import "fmt" // Imports the "fmt" package for formatted I/O
    
    func main() { // The entry point of the program
        fmt.Println("Hello, Go 1.22!") // Prints a string to the console
    }
  4. Run Your Program: From your project directory (myapp), execute the program:

    # Run the Go program
    go run main.go

    Output:

    Hello, Go 1.22!

Core Concepts

Understanding these fundamental concepts is crucial for working effectively with Go.

ConceptDescription
PackagesGo programs are organized into packages. A main package and main function define an executable program. Other packages are libraries.
ImportsThe import keyword brings other packages into scope. Standard library packages (like fmt) are commonly used.
FunctionsBlocks of code designed to perform a specific task. main() is the entry point. Functions can accept arguments and return values.
VariablesExplicitly declared with var or short declaration :=. Statically typed but often inferred. Zero-valued by default.
TypesGo is statically typed. Basic types include int, float64, bool, string. Composite types are struct, array, slice, map, interface, chan.
GoroutinesLightweight, independently executing functions. Go’s concurrency model based on Communicating Sequential Processes (CSP).
ChannelsTyped conduits through which goroutines can send and receive values, ensuring safe communication and synchronization.
Error HandlingGo functions return errors explicitly as the last return value. Errors are checked with if err != nil.

Essential Commands / API / Syntax

This section covers the most common Go syntax and commands you’ll use daily.

Working with the go Command

# Compile and run a Go program
go run main.go

# Compile a Go program into an executable binary
go build -o myprogram main.go
# ./myprogram (on Linux/macOS) or myprogram.exe (on Windows)

# Download and install a package (usually handled by go mod tidy)
go get github.com/some/package

# Add missing and remove unused module dependencies
go mod tidy

# Format your Go code automatically (use often!)
go fmt ./...

# Run tests
go test ./...

# View package documentation in your browser
go doc fmt.Println

Variables and Constants

package main

import "fmt"

func main() {
    // Declare a variable, zero-valued by default (e.g., int is 0, string is "")
    var name string
    name = "Alice"
    fmt.Println("Name:", name)

    // Declare and initialize
    var age int = 30
    fmt.Println("Age:", age)

    // Short variable declaration (type inferred), only inside functions
    city := "New York"
    fmt.Println("City:", city)

    // Multiple variable declarations
    var (
        height = 1.75
        weight = 70.5
    )
    fmt.Println("Height:", height, "Weight:", weight)

    // Constants
    const PI = 3.14159
    const GREETING = "Hello"
    fmt.Println("PI:", PI, "Greeting:", GREETING)

    // Untyped constants
    const untypedConst = 100 // Type determined by usage
    var typedVar int = untypedConst
    fmt.Println("Typed var:", typedVar)
}

Basic Types

package main

import "fmt"

func main() {
    // Integers
    var i int = 10         // Default int size varies by system (32 or 64-bit)
    var j int32 = -20      // 32-bit integer
    var k uint = 100       // Unsigned integer
    fmt.Printf("i: %T %v, j: %T %v, k: %T %v\n", i, i, j, j, k, k)

    // Floating-point numbers
    var f1 float32 = 3.14  // Single-precision float
    var f2 float64 = 3.1415926535 // Double-precision float (default for literals)
    fmt.Printf("f1: %T %v, f2: %T %v\n", f1, f1, f2, f2)

    // Booleans
    var b1 bool = true
    b2 := false
    fmt.Printf("b1: %T %v, b2: %T %v\n", b1, b1, b2, b2)

    // Strings (UTF-8 encoded)
    var s1 string = "Hello, Go"
    s2 := `Multi-line
string literal`
    fmt.Printf("s1: %T %q, s2: %T %q\n", s1, s1, s2, s2)
    fmt.Println("Length of s1:", len(s1))
}

Control Flow: if, for, switch

package main

import "fmt"

func main() {
    // If-Else
    x := 10
    if x > 5 {
        fmt.Println("x is greater than 5")
    } else if x == 5 {
        fmt.Println("x is 5")
    } else {
        fmt.Println("x is less than 5")
    }

    // If with a short statement
    if y := 20; y%2 == 0 { // y is scoped to the if-else block
        fmt.Println("y is even")
    } else {
        fmt.Println("y is odd")
    }

    // For loop (Go's only loop construct)
    // Basic for loop (like C's for)
    for i := 0; i < 3; i++ {
        fmt.Println("For loop iteration:", i)
    }

    // For as a while loop
    sum := 1
    for sum < 10 {
        sum += sum
        fmt.Println("Sum:", sum)
    }

    // Infinite loop (use with break or return)
    // for {
    //     fmt.Println("Infinite loop - press Ctrl+C to stop")
    // }

    // For-range over slices, arrays, strings, maps, channels
    numbers := []int{10, 20, 30}
    for index, value := range numbers {
        fmt.Printf("Index: %d, Value: %d\n", index, value)
    }

    // Go 1.22: For-range over integers
    fmt.Println("Countdown:")
    for i := range 3 { // i will be 0, 1, 2
        fmt.Println(3 - i)
    }

    // Switch statement
    day := "Monday"
    switch day {
    case "Saturday", "Sunday":
        fmt.Println("It's the weekend!")
    case "Monday":
        fmt.Println("It's Monday, time to work.")
    default:
        fmt.Println("It's a weekday.")
    }

    // Switch without a condition (like if/else if)
    score := 85
    switch {
    case score >= 90:
        fmt.Println("Grade A")
    case score >= 80:
        fmt.Println("Grade B")
    default:
        fmt.Println("Grade C or lower")
    }
}

Functions

package main

import "fmt"

// Function that takes two integers and returns their sum
func add(a int, b int) int {
    return a + b
}

// Function with named return values
func subtract(a, b int) (result int) { // Type can be omitted for consecutive same-type params
    result = a - b
    return // "naked" return, returns the named result variable
}

// Function returning multiple values (often for value and error)
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("cannot divide by zero")
    }
    return a / b, nil
}

// Variadic function (takes a variable number of arguments)
func sumAll(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

func main() {
    fmt.Println("5 + 3 =", add(5, 3))
    fmt.Println("10 - 4 =", subtract(10, 4))

    quotient, err := divide(10.0, 2.0)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("10 / 2 =", quotient)
    }

    _, err = divide(10.0, 0.0) // Ignore the first return value
    if err != nil {
        fmt.Println("Error:", err)
    }

    fmt.Println("Sum of 1, 2, 3:", sumAll(1, 2, 3))
    fmt.Println("Sum of 5, 10, 15, 20:", sumAll(5, 10, 15, 20))

    // Anonymous function (closure)
    greeter := func(name string) {
        fmt.Println("Hello,", name)
    }
    greeter("World")
}

Arrays, Slices, Maps

package main

import "fmt"

func main() {
    // Arrays: Fixed-size sequence of elements of the same type
    var a [3]int            // Declares an array of 3 integers, initialized to [0, 0, 0]
    a[0] = 10
    a[1] = 20
    a[2] = 30
    fmt.Println("Array a:", a, "Length:", len(a))

    b := [3]int{1, 2, 3}    // Declare and initialize
    fmt.Println("Array b:", b)

    c := [...]int{4, 5, 6, 7} // Compiler infers size
    fmt.Println("Array c:", c, "Length:", len(c))

    // Slices: Dynamic, flexible view into elements of an array
    // Slices are much more common than arrays in Go
    var s []int             // Declares a nil slice
    fmt.Println("Nil slice s:", s, "Length:", len(s), "Capacity:", cap(s))

    s = append(s, 10)       // Add elements
    s = append(s, 20, 30)
    fmt.Println("Slice s after append:", s, "Length:", len(s), "Capacity:", cap(s))

    // Make a slice with initial length and capacity
    // make([]type, length, capacity)
    cities := make([]string, 2, 5) // len=2, cap=5
    cities[0] = "London"
    cities[1] = "Paris"
    fmt.Println("Cities slice:", cities, "Length:", len(cities), "Capacity:", cap(cities))
    cities = append(cities, "Tokyo") // Capacity might expand if needed
    fmt.Println("Cities slice after append:", cities, "Length:", len(cities), "Capacity:", cap(cities))

    // Slice literals
    numbers := []int{1, 2, 3, 4, 5}
    fmt.Println("Numbers slice:", numbers)

    // Slicing (creates a new slice pointing to the same underlying array)
    subSlice := numbers[1:4] // elements from index 1 (inclusive) to 4 (exclusive) -> [2, 3, 4]
    fmt.Println("Sub-slice:", subSlice)

    // Maps: Unordered collections of key-value pairs
    // make(map[keyType]valueType)
    temperatures := make(map[string]float64)
    temperatures["NY"] = 25.5
    temperatures["LA"] = 30.1
    fmt.Println("Temperatures map:", temperatures)

    // Map literal
    ages := map[string]int{
        "Alice": 30,
        "Bob":   25,
    }
    fmt.Println("Ages map:", ages)

    // Accessing a value
    fmt.Println("Alice's age:", ages["Alice"])

    // Check if a key exists
    if val, ok := ages["Charlie"]; ok {
        fmt.Println("Charlie's age:", val)
    } else {
        fmt.Println("Charlie not found.")
    }

    // Delete an element
    delete(ages, "Bob")
    fmt.Println("Ages after deleting Bob:", ages)

    // Iterate over map
    for name, age := range ages {
        fmt.Printf("%s is %d years old\n", name, age)
    }
}

Structs and Methods

package main

import "fmt"

// Define a struct (a collection of fields)
type Person struct {
    Name    string
    Age     int
    City    string
}

// Method associated with the Person type
// (p Person) is the receiver
func (p Person) Greet() string {
    return fmt.Sprintf("Hello, my name is %s and I am %d years old.", p.Name, p.Age)
}

// Method that modifies a field (requires a pointer receiver)
func (p *Person) HappyBirthday() {
    p.Age++
}

func main() {
    // Create a struct instance
    person1 := Person{"Alice", 30, "London"}
    fmt.Println("Person 1:", person1)
    fmt.Println(person1.Greet())

    // Access fields
    fmt.Println("Person 1's city:", person1.City)

    // Create a struct instance with named fields
    person2 := Person{
        Name: "Bob",
        Age:  25,
        City: "Paris",
    }
    fmt.Println("Person 2:", person2)

    // Use a method to modify the struct
    person2.HappyBirthday() // Automatically takes the address of person2
    fmt.Println("Person 2 after birthday:", person2)

    // Anonymous struct
    anonUser := struct {
        ID   int
        Name string
    }{
        ID:   1,
        Name: "Guest",
    }
    fmt.Println("Anonymous user:", anonUser)
}

Pointers

package main

import "fmt"

func main() {
    // Declare a variable
    num := 10
    fmt.Println("Value of num:", num) // Output: 10

    // Declare a pointer to num
    var p *int // p is a pointer to an integer
    p = &num   // Assign the memory address of num to p
    fmt.Println("Address of num (&num):", &num) // Output: (memory address)
    fmt.Println("Value of p (address it holds):", p) // Output: (same memory address)
    fmt.Println("Value pointed to by p (*p):", *p)   // Dereference p to get the value -> Output: 10

    // Change value through pointer
    *p = 20
    fmt.Println("Value of num after pointer modification:", num) // Output: 20

    // Creating a new pointer with `new`
    newIntPtr := new(int) // Allocates memory for an int, returns a pointer to it, zero-valued
    fmt.Println("Value pointed to by newIntPtr:", *newIntPtr) // Output: 0
    *newIntPtr = 100
    fmt.Println("Value pointed to by newIntPtr after assignment:", *newIntPtr) // Output: 100
}

Error Handling

Go uses explicit error return values, typically as the last return value, and checks them using if err != nil.

package main

import (
	"errors"
	"fmt"
)

// Function that might return an error
func doSomethingRisky(input int) (string, error) {
    if input < 0 {
        return "", errors.New("input cannot be negative") // Create a new error
    }
    if input > 100 {
        return "", fmt.Errorf("input %d is too large", input) // Format an error
    }
    return fmt.Sprintf("Processed input: %d", input), nil // Return nil for no error
}

func main() {
    result, err := doSomethingRisky(50)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Success:", result)
    }

    result, err = doSomethingRisky(-10)
    if err != nil {
        fmt.Println("Error:", err) // Output: Error: input cannot be negative
    } else {
        fmt.Println("Success:", result)
    }

    result, err = doSomethingRisky(200)
    if err != nil {
        fmt.Println("Error:", err) // Output: Error: input 200 is too large
    } else {
        fmt.Println("Success:", result)
    }
}

Packages and Imports

// main.go (in myapp/ directory)
package main

import (
	"fmt"        // Standard library package for formatted I/O
	"myapp/utils" // Local package, relative path from module root
	"rsc.io/quote" // Third-party package (after `go get rsc.io/quote`)
)

func main() {
    fmt.Println("Hello from main!")
    fmt.Println(utils.SayHello("Go Developer")) // Call function from local package
    fmt.Println(quote.Go())                      // Call function from third-party package
}

// utils/greetings.go (in myapp/utils/ directory)
package utils // Declares this file belongs to the 'utils' package

import "fmt"

// SayHello is an exported function (starts with a capital letter)
func SayHello(name string) string {
    return fmt.Sprintf("Greetings from utils package, %s!", name)
}

To run the above example:

  1. Create myapp/main.go
  2. Create myapp/utils/greetings.go
  3. Run go mod init myapp in the myapp directory.
  4. Run go get rsc.io/quote
  5. Run go run main.go

Common Patterns

Reading Command-Line Arguments

package main

import (
	"fmt"
	"os" // Provides access to operating system functionality
)

func main() {
    // os.Args is a slice of strings, where os.Args[0] is the program name
    if len(os.Args) > 1 {
        fmt.Println("Arguments provided:")
        for i, arg := range os.Args[1:] { // Iterate over arguments starting from the second one
            fmt.Printf("Arg %d: %s\n", i+1, arg)
        }
    } else {
        fmt.Println("No command-line arguments provided.")
    }
}

Run:

go run main.go hello world
# Output:
# Arguments provided:
# Arg 1: hello
# Arg 2: world

Simple HTTP Server

package main

import (
	"fmt"
	"net/http" // Standard library for HTTP clients and servers
)

func main() {
    // Handle requests to the root path "/"
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, you've hit %s\n", r.URL.Path)
    })

    // Handle requests to "/greet"
    http.HandleFunc("/greet", func(w http.ResponseWriter, r *http.Request) {
        name := r.URL.Query().Get("name") // Get query parameter "name"
        if name == "" {
            name = "Guest"
        }
        fmt.Fprintf(w, "Greetings, %s!\n", name)
    })

    fmt.Println("Server starting on port 8080...")
    // Start the server on port 8080
    // http.ListenAndServe blocks until the server is shut down
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        fmt.Println("Server failed:", err)
    }
}

Run the server: go run main.go Then open your browser or use curl:

  • http://localhost:8080
  • http://localhost:8080/greet
  • http://localhost:8080/greet?name=Alice

Concurrency with Goroutines and Channels

package main

import (
	"fmt"
	"time"
)

// Worker function that runs as a goroutine
func worker(id int, messages <-chan string, results chan<- string) {
    for msg := range messages {
        fmt.Printf("Worker %d received: %s\n", id, msg)
        time.Sleep(time.Second) // Simulate work
        results <- fmt.Sprintf("Worker %d finished: %s", id, msg)
    }
}

func main() {
    // Create channels for communication
    messages := make(chan string) // Unbuffered channel for messages
    results := make(chan string)  // Unbuffered channel for results

    // Start multiple workers as goroutines
    for i := 1; i <= 3; i++ {
        go worker(i, messages, results)
    }

    // Send messages to workers
    for i := 1; i <= 5; i++ {
        msg := fmt.Sprintf("Task %d", i)
        messages <- msg
        fmt.Println("Sent:", msg)
    }
    close(messages) // Close the messages channel when done sending

    // Collect results from workers
    for i := 1; i <= 5; i++ {
        res := <-results
        fmt.Println("Received result:", res)
    }
    close(results) // Close the results channel (optional here, as main exits)

    fmt.Println("All tasks completed.")
}

Gotchas & Tips

  • No while loop: Go only has the for keyword. You can use for <condition> for a while loop or for {} for an infinite loop.
  • Unused Variables are Compile Errors: Go’s compiler is strict. Declared variables, imported packages, or functions that are not used will result in a compile-time error. This keeps code clean.
  • Explicit Error Handling: Go doesn’t have try-catch blocks. Errors are returned as the last value of a function, and you must explicitly check if err != nil. This encourages robust error management.
  • Slices vs. Arrays: Understand the difference. Arrays are fixed-size, while slices are dynamic views over underlying arrays. Most Go code uses slices. When passing slices to functions, they are passed by value (a copy of the slice header), but the underlying array is shared, so modifications to elements are visible to the caller. Appending to a slice might reallocate its underlying array if capacity is exceeded, potentially changing its backing array.
  • Structs and Pointers: When defining methods that need to modify the receiver (the struct instance), use a pointer receiver (func (p *Person)) instead of a value receiver (func (p Person)). Value receivers operate on a copy.
  • Go 1.22 Loop Variable Behavior: Prior to Go 1.22, loop variables were reused across iterations, which could lead to subtle bugs when using them in closures (e.g., goroutines launched inside a loop). As of Go 1.22, loop variables are created anew for each iteration, solving this common pitfall.
    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	fmt.Println("Go 1.22 loop variable behavior:")
    	for i := 0; i < 3; i++ {
    		go func() {
    			// In Go 1.22+, 'i' here correctly captures the value of 'i' for each iteration
    			// In earlier versions, this would often print '3' multiple times
    			fmt.Println(i)
    		}()
    	}
    	time.Sleep(time.Millisecond * 10) // Give goroutines time to run
    }
  • Go fmt for output: fmt.Println() for simple output, fmt.Printf() for formatted output (like C’s printf), and fmt.Sprintf() to format a string without printing it.
  • Zero Values: All variables in Go are initialized to their “zero value” if not explicitly assigned. For example, int is 0, bool is false, string is "" (empty string), pointers are nil, slices and maps are nil.

Next Steps

  • The Go Tour: The official interactive tutorial to learn Go. Highly recommended for beginners: go.dev/tour/
  • Effective Go: A document that gives tips for writing clear, idiomatic Go code: go.dev/doc/effective_go
  • Official Go Documentation: The definitive resource for the language and standard library: go.dev/doc/
  • Go Modules Reference: Deep dive into dependency management: go.dev/ref/mod

Source: z2h.fyi/cheatsheets/go-basics — Zero to Hero cheatsheets for developers.

Source: z2h.fyi/cheatsheets/go-language-basics — Zero to Hero cheatsheets for developers.