Go Basics
Program Structure
package main
import "fmt"
func main() {
fmt.Println("hello, world")
}
package main declares an executable. func main() is the entry point. No return value, no arguments — the OS exit code comes from os.Exit().
Variables & Declarations
// Short declaration — type inferred, only inside functions
name := "Evan"
count := 42
ratio := 3.14
// Explicit declaration — required at package level
var version string = "1.0.0"
var debug bool // zero value: false
// Multiple declaration block
var (
host = "localhost"
port = 8080
)
:= declares and assigns in one step. The compiler infers the type from the right-hand side. You cannot use := outside a function body — package-level variables require var.
Constants
const Pi = 3.14159265
const (
StatusOK = 200
StatusNotFound = 404
)
// iota — auto-incrementing constant generator
const (
Read = 1 << iota // 1
Write // 2
Execute // 4
)
Constants must be expressible at compile time. iota resets to 0 in each const block and increments by 1 per line — ideal for bitmasks and enumerations.
Imports
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
log "github.com/sirupsen/logrus" // alias import
_ "github.com/lib/pq" // blank import — side effects only
)
The blank import _ runs the package’s init() function without making its exports available. Common for database drivers that register themselves.
Control Flow
if err := doSomething(); err != nil {
log.Fatal(err)
}
// err is not accessible here — scoped to the if block
The init statement (err := …) scopes the variable to the if/else chain. This keeps error variables from leaking into the outer scope.
switch runtime.GOOS {
case "linux":
fmt.Println("Linux")
case "darwin":
fmt.Println("macOS")
default:
fmt.Println("other")
}
// Type switch
switch v := i.(type) {
case int:
fmt.Printf("integer: %d\n", v)
case string:
fmt.Printf("string: %q\n", v)
}
Go switches do not fall through by default — each case breaks automatically. Use fallthrough explicitly if needed. Type switches use .(type) to branch on the concrete type of an interface value.
// C-style
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// while-style
for count > 0 {
count--
}
// infinite
for {
// break or return to exit
}
// range over slice
for i, v := range []string{"a", "b", "c"} {
fmt.Printf("%d: %s\n", i, v)
}
for is Go’s only loop construct. range iterates slices, maps, strings, and channels. Use _ to discard index or value: for _, v := range items.
Printing & Formatting
name := "Evan"
age := 30
pi := 3.14159
fmt.Printf("Name: %s, Age: %d\n", name, age) // string, integer
fmt.Printf("Pi: %.2f\n", pi) // float with precision
fmt.Printf("Type: %T\n", pi) // type name
fmt.Printf("Value: %v\n", pi) // default format
fmt.Printf("Struct: %+v\n", config) // struct with field names
fmt.Printf("Go syntax: %#v\n", config) // Go-syntax representation
%v is the catch-all verb. %+v adds field names for structs. %#v produces Go-syntax output useful for debugging.
Zero Values
var i int // 0
var f float64 // 0.0
var b bool // false
var s string // "" (empty string)
var p *int // nil
var sl []int // nil (but len/cap work: both return 0)
var m map[string]int // nil (reads return zero value, writes panic)
Go does not have "undefined" or "null" in the JavaScript/Python sense. Every variable has a predictable zero value. A nil map can be read from safely but panics on write — always use make() before writing to a map.