1. Variables & Types
Declaration
package main
import "fmt"
func main() {
// var keyword with explicit type
var name string = "Alice"
var age int = 30
// Type inference
var score = 95.5
// Short declaration (most common, only inside functions)
city := "Tokyo"
isActive := true
fmt.Println(name, age, score, city, isActive)
}
Basic Types
| Type | Examples | Zero Value |
|---|---|---|
| bool | true, false |
false |
| int, int8/16/32/64 | 42, -7 |
0 |
| float32, float64 | 3.14, 2.718 |
0 |
| string | "hello" |
"" |
| byte (uint8) | 'A' |
0 |
| rune (int32) | 'δΈ' (Unicode code point) |
0 |
Type Conversion & Constants
// Go requires explicit type conversion (no implicit casting)
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
// Constants
const Pi = 3.14159
const (
StatusOK = 200
StatusError = 500
)
// iota: auto-incrementing constant generator
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
)
π‘ Go has zero values: every variable is initialized to its type's zero value if not explicitly assigned. No "undefined" or "null" surprises.
2. Strings
package main
import (
"fmt"
"strings"
)
func main() {
// Strings are immutable UTF-8 byte sequences
s := "Hello, World!"
// String operations via strings package
fmt.Println(strings.ToUpper(s)) // "HELLO, WORLD!"
fmt.Println(strings.Contains(s, "World")) // true
fmt.Println(strings.Split("a,b,c", ",")) // [a b c]
fmt.Println(strings.Join([]string{"a", "b"}, "-")) // "a-b"
// Formatting with fmt.Sprintf
name := "Go"
version := 1.24
msg := fmt.Sprintf("%s version %.2f", name, version)
fmt.Println(msg) // "Go version 1.24"
// Raw strings (backticks) β no escape processing
raw := `Line 1\nStill line 1
Line 2`
fmt.Println(raw)
// rune vs byte
chinese := "δ½ ε₯½"
fmt.Println(len(chinese)) // 6 (bytes)
fmt.Println(len([]rune(chinese))) // 2 (characters)
// Iterate by rune
for i, r := range chinese {
fmt.Printf("index=%d rune=%c\n", i, r)
}
}
π‘ len(s) returns byte count. For character count, use len([]rune(s)) or utf8.RuneCountInString(s).
3. Arrays & Slices
package main
import "fmt"
func main() {
// Array: fixed size, rarely used directly
var arr [3]int = [3]int{1, 2, 3}
fmt.Println(arr) // [1 2 3]
// Slice: dynamic, backed by an array (this is what you'll use)
nums := []int{10, 20, 30}
fmt.Println(nums) // [10 20 30]
fmt.Println(len(nums)) // 3 (length)
fmt.Println(cap(nums)) // 3 (capacity)
// make: create slice with length and capacity
s := make([]int, 0, 10) // len=0, cap=10
// append: add elements (may allocate new backing array)
s = append(s, 1, 2, 3)
fmt.Println(s) // [1 2 3]
// Slice operations (half-open interval)
data := []int{0, 1, 2, 3, 4, 5}
fmt.Println(data[1:4]) // [1 2 3]
fmt.Println(data[:3]) // [0 1 2]
fmt.Println(data[3:]) // [3 4 5]
// Copy
dst := make([]int, len(data))
copy(dst, data)
}
π‘ Slices are references to underlying arrays. Modifying a sub-slice affects the original. Use copy() when you need an independent copy.
4. Maps
package main
import "fmt"
func main() {
// Declaration and initialization
scores := map[string]int{
"Alice": 95,
"Bob": 87,
}
// Create with make
ages := make(map[string]int)
// CRUD operations
ages["Alice"] = 30 // Create / Update
age := ages["Alice"] // Read
delete(ages, "Alice") // Delete
// Comma-ok pattern: check if key exists
val, ok := scores["Charlie"]
if !ok {
fmt.Println("Charlie not found, val =", val) // val is 0 (zero value)
}
// Iteration (order is NOT guaranteed)
for name, score := range scores {
fmt.Printf("%s: %d\n", name, score)
}
// Length
fmt.Println(len(scores)) // 2
}
π Map Comparison
Python: dict |
JS: Map / Object |
Java: HashMap |
Go: map[K]V
5. Control Flow
if / else
// Standard if/else
if x > 10 {
fmt.Println("big")
} else if x > 5 {
fmt.Println("medium")
} else {
fmt.Println("small")
}
// if with init statement (variable scoped to if block)
if err := doSomething(); err != nil {
fmt.Println("error:", err)
}
for (the only loop in Go)
// Classic for loop
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// While-style
n := 1
for n < 100 {
n *= 2
}
// Infinite loop
for {
// break to exit
break
}
// range: iterate over slices, maps, strings, channels
fruits := []string{"apple", "banana", "cherry"}
for index, value := range fruits {
fmt.Printf("%d: %s\n", index, value)
}
// Ignore index with _
for _, fruit := range fruits {
fmt.Println(fruit)
}
switch (no break needed)
// switch: cases don't fall through by default
day := "Monday"
switch day {
case "Monday", "Tuesday":
fmt.Println("Early week")
case "Friday":
fmt.Println("TGIF!")
default:
fmt.Println("Other day")
}
// Type switch
var val interface{} = 42
switch v := val.(type) {
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
}
defer
func readFile() {
f, err := os.Open("data.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close() // Runs when function returns (LIFO order)
// ... use f ...
}
π‘ defer is like finally in Java or with in Python β it guarantees cleanup even if the function panics.
6. Error Handling
Go uses explicit error returns instead of try/catch exceptions. The error interface is central to Go.
package main
import (
"errors"
"fmt"
"strconv"
)
// Functions return (result, error)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Custom error with fmt.Errorf (wrapping)
func parseAge(s string) (int, error) {
age, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("parseAge(%q): %w", s, err)
}
if age < 0 || age > 150 {
return 0, fmt.Errorf("age %d out of range", age)
}
return age, nil
}
// Custom error type
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error: %s - %s", e.Field, e.Message)
}
func main() {
// Always check errors
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result)
// Unwrap errors with errors.Is / errors.As
_, err = parseAge("abc")
var numErr *strconv.NumError
if errors.As(err, &numErr) {
fmt.Println("Number error:", numErr)
}
}
π Error Handling: Go vs try/catch
Java/Python/JS: try { ... } catch (Error e) { ... }
Go: result, err := doWork(); if err != nil { ... }
Go's approach is verbose but explicit β you always know which function can fail and must handle the error.
π Chapter Summary
Short Declaration :=
Most common way to declare variables inside functions. Type is inferred automatically.
Zero Values
Every type has a zero value: 0, "", false, nil. No uninitialized variables.
Slices over Arrays
Use slices ([]T) for dynamic collections. Arrays ([N]T) are fixed-size and rarely used directly.
for is the Only Loop
No while or do-while. for handles all looping patterns including range iteration.
Comma-ok Pattern
val, ok := m[key] safely checks map access. A core Go idiom.
Explicit Errors
Return (result, error) and check with if err != nil. No hidden exceptions.