
Golang Cheatsheet: Complete Syntax Guide
1. Package Declaration & Imports
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("Hello, World!")
}- package main — every Go file belongs to a package. main is special: it tells Go this is an executable program (not a library).
- import — brings in other packages. Use parentheses to import multiple packages at once instead of repeating import on separate lines.
- func main() — the entry point. When you run a Go program, execution starts here.
- Unused imports or unused variables cause compile errors in Go — this is intentional, to keep code clean.
2. Variables & Data Types
var name string = "Alice" // explicit type
var age = 25 // type inferred
city := "Jakarta" // shorthand declaration (inside functions only)
var x, y int = 1, 2 // multiple variables, same type
a, b := 5, "hello" // multiple variables, mixed types
const Pi = 3.14159 // constant, cannot be changed- var — standard way to declare a variable. Can specify type explicitly or let Go infer it from the value.
- := — shorthand operator that declares and assigns in one step. Only works inside functions, not at package level.
- const — declares a value that cannot be reassigned later. Constants must be known at compile time.
- Go is statically typed: once a variable's type is set, it can't change.
Basic types:
| Type | Description | Example |
|---|---|---|
| int, int8, int32, int64 | Whole numbers | 10 |
| float32, float64 | Decimal numbers | 3.14 |
| string | Text | "hello" |
| bool | True/false | true |
| byte | Alias for uint8 | 'A' |
| rune | Alias for int32, represents a Unicode character | 'あ' |
3. Control Structures
If / Else
if age >= 18 {
fmt.Println("Adult")
} else if age >= 13 {
fmt.Println("Teenager")
} else {
fmt.Println("Child")
}
// If with a short statement before the condition
if err := doSomething(); err != nil {
fmt.Println("Error occurred")
}- No parentheses needed around the condition, but curly braces {} are mandatory, even for single-line bodies.
- You can run a short statement (like a variable assignment) before the condition — that variable's scope is limited to the if/else block.
For Loop
Go has only one looping keyword: for. It handles all loop styles.
// Classic three-part loop
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// While-style loop (condition only)
n := 0
for n < 5 {
n++
}
// Infinite loop
for {
fmt.Println("runs forever")
break // must have a break somewhere to stop
}
// Range loop (iterate over slices, arrays, maps, strings)
nums := []int{10, 20, 30}
for index, value := range nums {
fmt.Println(index, value)
}- i := 0; i < 5; i++ — init statement, condition, post statement — same structure as C-style for loops but without parentheses.
- range — used to loop through collections. Returns index+value for slices/arrays, key+value for maps.
- break exits the loop early; continue skips to the next iteration.
Switch
switch day {
case "Mon", "Tue", "Wed", "Thu", "Fri":
fmt.Println("Weekday")
case "Sat", "Sun":
fmt.Println("Weekend")
default:
fmt.Println("Invalid day")
}
// Switch without a condition (acts like if-else chain)
switch {
case age < 13:
fmt.Println("Child")
case age < 20:
fmt.Println("Teenager")
default:
fmt.Println("Adult")
}- Unlike C or Java, Go's switch does not fall through by default — each case automatically breaks. Use the fallthrough keyword if you explicitly want fallthrough behavior.
- A case can match multiple values separated by commas.
- A switch with no expression works like a cleaner if-else if chain.
4. Functions
func add(a int, b int) int {
return a + b
}
// Shorthand when parameters share a type
func multiply(a, b int) int {
return a * b
}
// Multiple return values
func divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide by zero")
}
return a / b, nil
}
// Named return values
func rectangleArea(width, height float64) (area float64) {
area = width * height
return // "naked" return, uses the named variable
}
// Variadic functions (accepts any number of arguments)
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}- func name(params) returnType { } — basic structure.
- Go functions can return multiple values, most commonly used for returning a result alongside an error.
- Named return values let you declare the return variable in the function signature; return with no arguments sends back whatever those variables currently hold.
- ...int (variadic) lets a function accept a variable number of arguments, accessible inside as a slice.
5. Structs & Methods
type Person struct {
Name string
Age int
}
// Method with value receiver (works on a copy)
func (p Person) Greet() string {
return "Hi, I'm " + p.Name
}
// Method with pointer receiver (can modify the original)
func (p *Person) Birthday() {
p.Age++
}
p := Person{Name: "Alice", Age: 25}
p.Birthday()
fmt.Println(p.Age) // 26- type Name struct { } — defines a custom data type with named fields.
- Methods are functions attached to a type via a receiver — (p Person) or (p *Person) before the function name.
- Value receiver: the method gets a copy of the struct; changes inside don't affect the original.
- Pointer receiver (*Person): the method can modify the original struct's fields directly. Use this when you need mutation or want to avoid copying large structs.
6. Interfaces
type Greeter interface {
Greet() string
}
func printGreeting(g Greeter) {
fmt.Println(g.Greet())
}- An interface defines a set of method signatures — any type that implements those methods automatically satisfies the interface. There's no explicit implements keyword like in Java.
- This enables polymorphism: printGreeting can accept any type as long as it has a Greet() string method.
- The empty interface interface{} (or any in modern Go) can hold a value of any type.
7. Slices & Arrays
// Array: fixed size
var arr [3]int = [3]int{1, 2, 3}
// Slice: dynamic size, backed by an array
nums := []int{1, 2, 3}
nums = append(nums, 4) // adds an element
sub := nums[1:3] // slicing: index 1 up to (not including) 3- Arrays have a fixed length defined at declaration and rarely used directly in Go.
- Slices are far more common — they're flexible, resizable views over an underlying array.
- append() adds elements and returns a new slice (may reallocate memory if capacity is exceeded).
- slice[start:end] — slicing syntax, end is exclusive.
8. Maps
ages := map[string]int{
"Alice": 25,
"Bob": 30,
}
ages["Charlie"] = 28 // add/update
value, exists := ages["Dave"] // check existence
delete(ages, "Bob") // remove a key- map[KeyType]ValueType — a key-value data structure, similar to a dictionary/hashmap.
- Accessing a missing key returns the zero value for that type (not an error), so use the value, exists := map[key] pattern to safely check if a key is present.
- delete(map, key) removes an entry.
9. Pointers
x := 10
p := &x // p holds the memory address of x
*p = 20 // dereference: change the value x points to
fmt.Println(x) // 20- &variable — gets the memory address (pointer) of a variable.
- *pointer — dereferences a pointer, accessing/modifying the value it points to.
- Go has pointers but no pointer arithmetic (unlike C), making them safer.
10. Goroutines & Channels (Concurrency)
func sayHello() {
fmt.Println("Hello from goroutine!")
}
func main() {
go sayHello() // runs concurrently, doesn't block main
ch := make(chan string)
go func() {
ch <- "data sent" // send value into channel
}()
msg := <-ch // receive value from channel (blocks until data arrives)
fmt.Println(msg)
}- go functionCall() — launches a goroutine, a lightweight thread managed by the Go runtime (not an OS thread).
- chan Type — a channel is a typed pipe for goroutines to communicate and synchronize.
- ch <- value sends a value into the channel; <-ch receives a value. Both operations block until the other side is ready (for unbuffered channels).
- This is Go's core concurrency model: "don't communicate by sharing memory; share memory by communicating."
11. Error Handling
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
// Wrapping errors
if err != nil {
return fmt.Errorf("operation failed: %w", err)
}- Go has no exceptions. Functions that can fail return an error as their last value.
- The idiomatic pattern is if err != nil { handle it } immediately after a function call.
- %w in fmt.Errorf wraps an existing error, preserving the error chain so it can be inspected later with errors.Is or errors.Unwrap.
12. Defer, Panic, Recover
func readFile() {
fmt.Println("opening file")
defer fmt.Println("closing file") // runs when function returns
panic("something went wrong")
}
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered from:", r)
}
}()
panic("boom")
}- defer schedules a function call to run right before the enclosing function returns — commonly used for cleanup (closing files, unlocking mutexes). Multiple defer calls run in last-in-first-out order.
- panic stops normal execution and starts unwinding the stack — used for unrecoverable errors.
- recover (only useful inside a deferred function) catches a panic and lets the program continue instead of crashing.
13. Packages & Modules
go mod init myproject # create a new module (go.mod file)
go get github.com/pkg/x # add an external dependency
go run main.go # compile and run immediately
go build # compile into a binary
go test # run tests in the current package
go fmt # auto-format code- go.mod tracks the module name, Go version, and dependencies — similar to package.json in Node.js.
- go build produces a standalone binary with no external runtime needed — a key reason Go is popular for deployment.
That's the full syntax rundown — every core building block from variables to concurrency. Want a follow-up cheatsheet on generics (Go 1.18+), the context package, or testing conventions?