Go Language Basics Cheatsheet
Master the fundamentals of Go (Golang) for rapid development, covering syntax, core concepts, and essential commands for version 1.22.
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.
-
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) orC:\Go(Windows), and configure yourPATHenvironment variable.
- macOS (Homebrew):
-
Verify Installation: Open a new terminal or command prompt and run:
# Check the Go version go versionYou should see output similar to
go version go1.22.x <os>/<arch>. -
Your First Go Program (Hello World): Create a new directory for your project:
# Create a project directory mkdir myapp cd myappInitialize a Go module (this manages dependencies):
# Initialize a Go module go mod init myappCreate a file named
main.gowith 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 } -
Run Your Program: From your project directory (
myapp), execute the program:# Run the Go program go run main.goOutput:
Hello, Go 1.22!
Core Concepts
Understanding these fundamental concepts is crucial for working effectively with Go.
| Concept | Description |
|---|---|
| Packages | Go programs are organized into packages. A main package and main function define an executable program. Other packages are libraries. |
| Imports | The import keyword brings other packages into scope. Standard library packages (like fmt) are commonly used. |
| Functions | Blocks of code designed to perform a specific task. main() is the entry point. Functions can accept arguments and return values. |
| Variables | Explicitly declared with var or short declaration :=. Statically typed but often inferred. Zero-valued by default. |
| Types | Go is statically typed. Basic types include int, float64, bool, string. Composite types are struct, array, slice, map, interface, chan. |
| Goroutines | Lightweight, independently executing functions. Go’s concurrency model based on Communicating Sequential Processes (CSP). |
| Channels | Typed conduits through which goroutines can send and receive values, ensuring safe communication and synchronization. |
| Error Handling | Go 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:
- Create
myapp/main.go - Create
myapp/utils/greetings.go - Run
go mod init myappin themyappdirectory. - Run
go get rsc.io/quote - 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:8080http://localhost:8080/greethttp://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
whileloop: Go only has theforkeyword. You can usefor <condition>for awhileloop orfor {}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-catchblocks. Errors are returned as the last value of a function, and you must explicitly checkif 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
fmtfor output:fmt.Println()for simple output,fmt.Printf()for formatted output (like C’sprintf), andfmt.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,
intis0,boolisfalse,stringis""(empty string), pointers arenil, slices and maps arenil.
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.