Summry
Complete Go Syntax Reference
Section titled “Complete Go Syntax Reference”This document explains the syntax of every major Go construct. Each example includes a definition, what problem it solves, when to use it, a usage demonstration, and its output (where applicable). After each section, you will find a link to the corresponding detailed chapter file (or a placeholder). Use this as your ultimate Go syntax guide.
1. Package and Import
Section titled “1. Package and Import”1.1 Package declaration
Section titled “1.1 Package declaration”package main- Definition: A package is a collection of Go source files.
package maindeclares the program’s entry point. - What it solves: Packages provide code organisation, namespace separation, and compilation units.
- When to use: Every Go file must start with a package declaration. Use
mainfor executables, any other name for reusable libraries.
Usage example:
Create a file main.go with the line above. Run go run main.go – it compiles without error (even if empty).
1.2 Import statement
Section titled “1.2 Import statement”import "fmt"import "os"
// Grouped importimport ( "fmt" "os")
// Aliasimport f "fmt"
// Blank import (side‑effect only)import _ "image/png"- Definition: An import statement makes exported identifiers of another package available.
- What it solves: It allows code reuse across packages without copying.
- When to use: Whenever you need functionality from the standard library or third‑party packages.
Usage example:
package main
import f "fmt"
func main() { f.Println("Hello")}Output:
Hello2. Variables and Constants
Section titled “2. Variables and Constants”2.1 Variable declaration (full form)
Section titled “2.1 Variable declaration (full form)”var x int = 42- Definition:
vardeclares one or more variables. The type and initial value are optional if the other is present. - What it solves: Variables store data that can change during program execution.
- When to use: When you need mutable storage; use full form when type is not obvious or you want to declare without initialisation.
Usage example:
var x int = 42fmt.Println(x)Output:
422.2 Short variable declaration (type inference)
Section titled “2.2 Short variable declaration (type inference)”y := 10- Definition:
:=declares and initialises a variable, inferring the type from the right‑hand side. - What it solves: It reduces verbosity and makes code cleaner when the type is obvious.
- When to use: Inside functions (not at package level) for local variables.
Usage example:
y := 10fmt.Printf("%T %v", y, y)Output:
int 102.3 Multiple variables
Section titled “2.3 Multiple variables”var a, b int = 1, 2c, d := 3, "hello"- Definition: You can declare multiple variables in a single line, optionally with different types.
- What it solves: It allows concise initialisation of several variables at once.
- When to use: When you need several variables that are related or initialised together.
Usage example:
var a, b int = 1, 2c, d := 3, "hello"fmt.Println(a, b, c, d)Output:
1 2 3 hello2.4 Constants
Section titled “2.4 Constants”const Pi = 3.14const ( StatusOK = 200 StatusNotFound = 404)- Definition: Constants are immutable values known at compile time.
- What it solves: They prevent accidental modification and enable compiler optimisations.
- When to use: For mathematical constants, status codes, configuration values that never change.
Usage example:
const Pi = 3.14fmt.Println(Pi)Output:
3.142.5 Iota enumerations
Section titled “2.5 Iota enumerations”const ( Sunday = iota // 0 Monday // 1 Tuesday // 2)- Definition:
iotais a predeclared identifier that generates sequential integer constants. - What it solves: It simplifies creating enumerations without manual numbering.
- When to use: When you need a set of related constants that should have sequential values (days, states, flags).
Usage example:
const ( Sunday = iota Monday Tuesday)fmt.Println(Sunday, Monday, Tuesday)Output:
0 1 2→ Chapter 2.3: Variables and constants
→ Chapter 2.4: Constants and iota enums
3. Basic I/O
Section titled “3. Basic I/O”var name stringfmt.Print("Enter name: ")fmt.Scan(&name)fmt.Printf("Hello, %s\n", name)- Definition: Basic input/output functions from the
fmtpackage for printing and scanning. - What it solves: It provides simple ways to interact with the user or log data.
- When to use: For command‑line tools, debugging, or simple user interaction.
Usage example:
package main
import "fmt"
func main() { var name string fmt.Print("Enter name: ") fmt.Scan(&name) fmt.Printf("Hello, %s\n", name)}Input:
Alice
Output:
Enter name: Hello, Alice4. Strings and Runes
Section titled “4. Strings and Runes”s := "Go"r := 'G' // rune literalfor i, ch := range s { fmt.Printf("%d: %c\n", i, ch)}- Definition: A string is a read‑only slice of bytes; a rune represents a Unicode code point.
- What it solves: Strings handle UTF‑8 text; runes allow character‑level manipulation.
- When to use: Strings for text, runes when you need to process individual characters (e.g., iterating over Unicode).
Usage example:
s := "Go"for i, ch := range s { fmt.Printf("%d: %c\n", i, ch)}Output:
0: G1: o→ Chapter 2.8: Strings and runes
5. Type Conversions and Number Parsing
Section titled “5. Type Conversions and Number Parsing”var f float64 = 3.14var i int = int(f) // explicit conversionnum, err := strconv.Atoi("42")- Definition: Type conversions change a value from one type to another; parsing converts strings to numbers.
- What it solves: They allow operations between different numeric types and interpret user input.
- When to use: When you need to mix types (e.g., int to float) or read numbers from strings.
Usage example:
package main
import ( "fmt" "strconv")
func main() { var f float64 = 3.14 i := int(f) fmt.Println(i)
num, err := strconv.Atoi("42") if err == nil { fmt.Println(num) }}Output:
342→ Chapter 2.6: Type conversions
→ Chapter 2.7: Number parsing
6. Control Flow
Section titled “6. Control Flow”6.1 if statement
Section titled “6.1 if statement”if x := 10; x > 5 { fmt.Println("large")} else { fmt.Println("small")}- Definition:
ifexecutes a block conditionally, optionally with a short statement before the condition. - What it solves: It provides branching logic based on boolean conditions.
- When to use: Whenever you need to make decisions in your code.
Usage example:
if x := 10; x > 5 { fmt.Println("large")} else { fmt.Println("small")}Output:
large→ Chapter 3.1: Conditionals if else
6.2 for loop – three forms
Section titled “6.2 for loop – three forms”Classic C‑style
for i := 0; i < 5; i++ { fmt.Println(i)}- Definition:
forwith init, condition, and post. - What it solves: It executes a block a fixed number of times with a counter.
- When to use: When you know the number of iterations in advance.
Usage example:
for i := 0; i < 5; i++ { fmt.Print(i, " ")}Output:
0 1 2 3 4While‑style
n := 0for n < 3 { fmt.Println(n) n++}- Definition:
forwith only the condition (likewhilein other languages). - What it solves: It loops until a condition becomes false.
- When to use: When the number of iterations depends on a dynamic condition.
Usage example:
n := 0for n < 3 { fmt.Print(n, " ") n++}Output:
0 1 2Infinite loop
for { // break or return to exit}- Definition:
forwith no condition loops forever. - What it solves: It creates a loop that runs until explicitly broken.
- When to use: For servers, event loops, or when exit condition is complex.
Usage example (break after 3 iterations):
i := 0for { if i >= 3 { break } fmt.Print(i, " ") i++}Output:
0 1 26.3 range – with underscore explanation
Section titled “6.3 range – with underscore explanation”nums := []int{10, 20, 30}for i, v := range nums { fmt.Println(i, v)}
// Ignoring indexfor _, v := range nums { fmt.Println(v)}- Definition:
rangeiterates over elements of slices, arrays, strings, maps, or channels. - What it solves: It provides a safe, convenient way to loop over collections without managing indices.
- When to use: Whenever you need to iterate over any collection type.
Usage example:
nums := []int{10, 20, 30}for i, v := range nums { fmt.Printf("index=%d value=%d\n", i, v)}Output:
index=0 value=10index=1 value=20index=2 value=30→ Chapter 3.4: Range over built in types
6.4 switch
Section titled “6.4 switch”switch day := 2; day {case 1: fmt.Println("Monday")case 2: fmt.Println("Tuesday")default: fmt.Println("Other")}
// Tagless switchswitch {case x > 0: fmt.Println("positive")case x < 0: fmt.Println("negative")}- Definition:
switchevaluates a value (or no value) and executes the first matching case. - What it solves: It simplifies long
if‑elsechains and provides multi‑way branching. - When to use: When comparing a single value against many possibilities, or for boolean conditions with multiple branches.
Usage example:
day := 2switch day {case 1: fmt.Println("Monday")case 2: fmt.Println("Tuesday")default: fmt.Println("Other")}Output:
Tuesday7. Defer, Panic, Recover
Section titled “7. Defer, Panic, Recover”7.1 defer
Section titled “7.1 defer”func readFile() error { f, err := os.Open("data.txt") if err != nil { return err } defer f.Close() // runs when function returns // ... process file ... return nil}- Definition:
deferschedules a function call to run after the surrounding function returns. - What it solves: It ensures cleanup (closing files, unlocking mutexes) even if the function returns early or panics.
- When to use: Any time you acquire a resource that must be released.
Usage example:
func main() { defer fmt.Println("world") fmt.Println("hello")}Output:
helloworld7.2 panic and recover
Section titled “7.2 panic and recover”defer func() { if r := recover(); r != nil { fmt.Println("Recovered:", r) }}()panic("something went wrong")- Definition:
panicstops normal execution;recoverregains control inside a deferred function. - What it solves: They handle truly exceptional situations (bugs, impossible states) that cannot be recovered normally.
- When to use: Only for unrecoverable errors; do not use as regular error handling.
Usage example:
package main
import "fmt"
func main() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered:", r) } }() panic("boom") fmt.Println("never printed")}Output:
Recovered: boom→ Chapter 3.8: Defer
→ Chapter 3.9: Panic and recover
8. Functions – Complete Syntax
Section titled “8. Functions – Complete Syntax”8.1 Basic function
Section titled “8.1 Basic function”func add(a int, b int) int { return a + b}- Definition: A function is a reusable block of code with parameters and return values.
- What it solves: It organises code, avoids duplication, and encapsulates logic.
- When to use: Any time you need to perform a specific task that may be called multiple times.
Usage example:
func add(a, b int) int { return a + b}fmt.Println(add(3, 5))Output:
88.2 Multiple return values
Section titled “8.2 Multiple return values”func div(a, b int) (int, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil}- Definition: Functions can return any number of values.
- What it solves: It enables returning both a result and an error, or multiple related values.
- When to use: Idiomatic error handling (result, error) or when a function naturally produces multiple outputs.
Usage example:
result, err := div(10, 2)if err == nil { fmt.Println(result)}Output:
58.3 Named return values
Section titled “8.3 Named return values”func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return // naked return (returns x, y)}- Definition: Return parameters can be named and used as local variables.
- What it solves: It documents the meaning of returns and allows naked returns.
- When to use: For short functions where names clarify intent; use sparingly.
Usage example:
a, b := split(17)fmt.Println(a, b)Output:
7 108.4 Variadic functions
Section titled “8.4 Variadic functions”func sum(nums ...int) int { total := 0 for _, n := range nums { total += n } return total}- Definition: Variadic parameters accept zero or more arguments of the same type.
- What it solves: It allows functions to work with an arbitrary number of arguments.
- When to use: For functions like
fmt.Printlnor when you don’t know the number of inputs.
Usage example:
fmt.Println(sum(1, 2, 3, 4))Output:
108.5 Closures (anonymous functions)
Section titled “8.5 Closures (anonymous functions)”adder := func(x int) int { return x + 5}fmt.Println(adder(10))- Definition: An anonymous function that can capture variables from its surrounding scope.
- What it solves: It enables functional programming patterns (callbacks, goroutines, custom sort predicates).
- When to use: When you need a small, one‑off function, especially inside another function or as a goroutine.
Usage example:
mult := func(a, b int) int { return a * b }fmt.Println(mult(3, 4))Output:
12→ Chapter 5.1: Function declaration
→ Chapter 5.2: Multiple return values
→ Chapter 5.3: Variadic functions
→ Chapter 5.4: Closures
9. Methods and Receivers – All Four Forms
Section titled “9. Methods and Receivers – All Four Forms”Method syntax
Section titled “Method syntax”func (receiver Type) MethodName(params) returnType { // body}- Definition: A method is a function with a receiver argument that attaches it to a type.
- What it solves: It allows object‑oriented style behaviour (attaching operations to data).
- When to use: When you want to associate behaviour with a custom type.
The four receiver forms
Section titled “The four receiver forms”| Form | Syntax | Access receiver | Modify original |
|---|---|---|---|
| Named value | (c T) | Yes (copy) | No |
| Unnamed value | (T) | No | No |
| Named pointer | (c *T) | Yes (original) | Yes |
| Unnamed pointer | (*T) | No | No (useless) |
Example of all four
Section titled “Example of all four”type Counter struct { value int}
// 1. Named value receiver (getter)func (c Counter) Value() int { return c.value}
// 2. Unnamed value receiver (only to satisfy interface)func (Counter) Description() string { return "a counter"}
// 3. Named pointer receiver (setter)func (c *Counter) Inc() { c.value++}
// 4. Unnamed pointer receiver – pointless, shown for completenessfunc (*Counter) NoOp() {}- What each solves:
- Named value: read‑only access, safe for concurrency.
- Unnamed value: satisfy interfaces without needing the receiver.
- Named pointer: modify the original struct, avoid copying large structs.
- Unnamed pointer: rarely useful.
Usage example:
c := Counter{value: 5}fmt.Println(c.Value()) // 5c.Inc()fmt.Println(c.Value()) // 6fmt.Println(c.Description())Output:
56a counter→ Chapter 5.5: Methods
→ Chapter 5.5.1: Receiver syntax
10. Error Handling
Section titled “10. Error Handling”10.1 Standard pattern
Section titled “10.1 Standard pattern”result, err := doSomething()if err != nil { // handle error}- Definition: The standard error handling pattern returns an error value alongside the result.
- What it solves: It makes errors explicit and forces the caller to handle them (or ignore them deliberately).
- When to use: Every function that can fail should return an error.
Usage example:
f, err := os.Open("nonexistent.txt")if err != nil { fmt.Println("Error:", err)}Output:
Error: open nonexistent.txt: no such file or directory10.2 Creating errors
Section titled “10.2 Creating errors”err1 := errors.New("something went wrong")err2 := fmt.Errorf("invalid value: %d", val)- Definition: Simple error creation using
errors.Neworfmt.Errorf. - What it solves: It provides error values with custom messages.
- When to use: When you need to return a simple error from a function.
Usage example:
err := errors.New("custom error")fmt.Println(err)Output:
custom error10.3 Custom error type
Section titled “10.3 Custom error type”type ValidationError struct { Field string Value interface{}}
func (e ValidationError) Error() string { return fmt.Sprintf("validation failed on %s: %v", e.Field, e.Value)}- Definition: A custom type implementing the
errorinterface. - What it solves: It allows attaching additional data and enables type assertions.
- When to use: When callers need to distinguish error types programmatically.
Usage example:
func validate(age int) error { if age < 0 { return ValidationError{Field: "age", Value: age} } return nil}err := validate(-5)fmt.Println(err)Output:
validation failed on age: -5→ Chapter 5.6: Error handling
→ Chapter 5.7: Custom errors
11. Structs – Declaration, Literals, Embedding
Section titled “11. Structs – Declaration, Literals, Embedding”11.1 Struct declaration
Section titled “11.1 Struct declaration”type Person struct { Name string Age int}- Definition: A struct is a composite type grouping fields together.
- What it solves: It organises related data into a single unit.
- When to use: Almost always for data records (e.g., database models, configuration).
Usage example:
p := Person{Name: "Alice", Age: 30}fmt.Println(p.Name, p.Age)Output:
Alice 3011.2 Struct literal
Section titled “11.2 Struct literal”p1 := Person{"Alice", 30} // order mattersp2 := Person{Name: "Bob", Age: 25} // named fields (preferred)p3 := &Person{Name: "Charlie", Age: 35} // pointer to struct- Definition: A literal creates a struct instance with initial field values.
- What it solves: It provides concise initialisation.
- When to use: Always prefer named fields for clarity and safety; use positional only when order is obvious.
Usage example:
p := &Person{Name: "David", Age: 40}fmt.Println(p.Name)Output:
David11.3 Struct embedding (composition)
Section titled “11.3 Struct embedding (composition)”type Employee struct { Person // embedded (no field name) Salary int}
e := Employee{Person: Person{Name: "David", Age: 40}, Salary: 50000}fmt.Println(e.Name) // promoted field- Definition: Embedding a struct without a field name promotes its fields and methods.
- What it solves: It provides composition (instead of inheritance) for code reuse.
- When to use: When you want to reuse fields/methods from another struct (e.g., model extension).
Usage example:
type Employee struct { Person Salary int}e := Employee{Person: Person{Name: "Eve", Age: 28}, Salary: 60000}fmt.Println(e.Name, e.Age, e.Salary)Output:
Eve 28 60000→ Chapter 4.4: Structs
→ Chapter 4.5: Struct embedding
12. Pointers
Section titled “12. Pointers”x := 42p := &x // p points to xfmt.Println(*p) // dereference: 42*p = 100 // change x through pointer- Definition: A pointer holds the memory address of another variable.
- What it solves: It allows functions to modify original variables and avoids copying large data.
- When to use: When you need to mutate the caller’s variable, or when passing large structs.
Usage example:
x := 10p := &x*p = 20fmt.Println(x)Output:
2013. Arrays and Slices
Section titled “13. Arrays and Slices”13.1 Array
Section titled “13.1 Array”b := [4]int{1, 2, 3, 4}- Definition: An array is a fixed‑length sequence of elements of the same type.
- What it solves: It provides contiguous, fixed‑size storage.
- When to use: Rarely; prefer slices unless you need the length to be part of the type (e.g., for checksums).
Usage example:
arr := [3]string{"a", "b", "c"}fmt.Println(arr[1])Output:
b13.2 Slice
Section titled “13.2 Slice”s := []int{1, 2, 3}s = append(s, 4)- Definition: A slice is a dynamically‑sized view into an array.
- What it solves: It provides flexible, resizable sequences.
- When to use: Almost always instead of arrays for lists of elements.
Usage example:
s := []int{1, 2, 3}s = append(s, 4)fmt.Println(s)Output:
[1 2 3 4]13.3 Make for slices, maps, channels
Section titled “13.3 Make for slices, maps, channels”m := make(map[string]int, 10)- Definition:
makeallocates and initialises slices, maps, or channels. - What it solves: It initialises the internal data structures (e.g., map buckets, slice array).
- When to use: When you need to specify initial capacity (for performance) or create a channel.
Usage example:
m := make(map[string]int, 5)m["key"] = 42fmt.Println(m["key"])Output:
42→ Chapter 4.1: Arrays
→ Chapter 4.2: Slices
14. Maps
Section titled “14. Maps”m := map[string]int{ "a": 1, "b": 2,}value, ok := m["c"] // ok is false if key not presentdelete(m, "a")- Definition: A map is an unordered collection of key‑value pairs.
- What it solves: It provides fast lookups, insertions, and deletions by key.
- When to use: When you need to associate values with unique keys (e.g., cache, dictionary).
Usage example:
m := map[string]int{"apple": 5, "banana": 3}if val, ok := m["apple"]; ok { fmt.Println(val)}delete(m, "banana")fmt.Println(m)Output:
5map[apple:5]15. Interfaces
Section titled “15. Interfaces”15.1 Interface declaration
Section titled “15.1 Interface declaration”type Greeter interface { Greet() string}- Definition: An interface defines a set of method signatures.
- What it solves: It enables polymorphism: a function can accept any type that implements the required methods.
- When to use: When you want to decouple code from concrete types (e.g., testing with mocks, pluggable designs).
15.2 Implementing an interface (implicit)
Section titled “15.2 Implementing an interface (implicit)”type Dog struct{}
func (d Dog) Greet() string { return "Woof!"}- Definition: A type implements an interface simply by having all the required methods (no
implementskeyword). - What it solves: It allows retroactive interface satisfaction and reduces coupling.
- When to use: Always; it’s the idiomatic Go way.
Usage example:
var g Greeter = Dog{}fmt.Println(g.Greet())Output:
Woof!15.3 Empty interface (any)
Section titled “15.3 Empty interface (any)”var anything interface{} // or 'any' (Go 1.18+)anything = 42anything = "hello"- Definition: An interface with zero methods; every type implements it.
- What it solves: It allows storing values of any type (like
void*in C orObjectin Java). - When to use: For containers that must hold arbitrary types (e.g.,
json.Unmarshal,fmt.Println).
Usage example:
var x any = 3.14fmt.Println(x)Output:
3.1415.4 Type assertion
Section titled “15.4 Type assertion”var i interface{} = "hello"s := i.(string) // assertion (panics if wrong)s, ok := i.(string) // safe assertion- Definition: Type assertion extracts the concrete value from an interface.
- What it solves: It allows switching from interface back to concrete type.
- When to use: When you need to perform type‑specific operations on an interface value.
Usage example:
var i interface{} = 42v, ok := i.(int)fmt.Println(v, ok)v2, ok := i.(string)fmt.Println(v2, ok)Output:
42 true false15.5 Type switch
Section titled “15.5 Type switch”switch v := i.(type) {case int: fmt.Println("int:", v)case string: fmt.Println("string:", v)default: fmt.Println("unknown")}- Definition: A type switch branches based on the concrete type of an interface.
- What it solves: It provides a cleaner way to handle multiple possible types.
- When to use: When you have an interface that could hold several different concrete types.
Usage example:
func describe(i interface{}) { switch v := i.(type) { case int: fmt.Println("int:", v) case string: fmt.Println("string:", v) default: fmt.Println("unknown") }}describe(42)describe("hello")Output:
int: 42string: hello→ Chapter 7.1: Interface declaration
→ Chapter 7.2: The empty interface
16. Generics (Go 1.18+)
Section titled “16. Generics (Go 1.18+)”16.1 Generic function
Section titled “16.1 Generic function”func Identity[T any](x T) T { return x}- Definition: A generic function can work with any type specified by a type parameter.
- What it solves: It eliminates code duplication for algorithms that are type‑agnostic.
- When to use: When you would otherwise write nearly identical functions for different types.
Usage example:
fmt.Println(Identity(42))fmt.Println(Identity("hello"))Output:
42hello16.2 Generic type
Section titled “16.2 Generic type”type Stack[T any] struct { items []T}
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v)}- Definition: A generic type can have type parameters for its fields and methods.
- What it solves: It allows creating data structures that work with any element type.
- When to use: For collections like stacks, queues, or any container that should be type‑safe.
Usage example:
s := Stack[int]{}s.Push(10)fmt.Println(s.items)Output:
[10]→ Chapter 8.1: Type parameters
17. Concurrency – Goroutines and Channels
Section titled “17. Concurrency – Goroutines and Channels”17.1 Goroutine
Section titled “17.1 Goroutine”go func() { fmt.Println("running concurrently")}()- Definition: A goroutine is a lightweight thread managed by the Go runtime.
- What it solves: It enables concurrent execution without heavy OS threads.
- When to use: For any concurrent task (e.g., handling requests, background work).
Usage example:
go func() { fmt.Println("goroutine")}()time.Sleep(10 * time.Millisecond) // allow goroutine to finishOutput:
goroutine17.2 Unbuffered channel
Section titled “17.2 Unbuffered channel”ch := make(chan int)go func() { ch <- 42 }()value := <-ch- Definition: An unbuffered channel synchronises send and receive operations.
- What it solves: It provides a communication mechanism that also synchronises goroutines.
- When to use: When you need to pass data and also ensure hand‑off (e.g., signalling).
Usage example:
ch := make(chan int)go func() { ch <- 100 }()fmt.Println(<-ch)Output:
10017.3 Buffered channel
Section titled “17.3 Buffered channel”ch := make(chan int, 2)ch <- 1ch <- 2- Definition: A buffered channel has a capacity; sends block only when full.
- What it solves: It reduces blocking and can act as a queue.
- When to use: When you want to decouple producer and consumer rates (e.g., request queue).
Usage example:
ch := make(chan int, 2)ch <- 1ch <- 2fmt.Println(<-ch)fmt.Println(<-ch)Output:
1217.4 Channel directions
Section titled “17.4 Channel directions”func send(ch chan<- int) { ch <- 42 } // send‑onlyfunc receive(ch <-chan int) { <-ch } // receive‑only- Definition: Channel directions restrict operations (send or receive only).
- What it solves: It enforces correct usage at compile time (e.g., a function that only sends).
- When to use: In function signatures to document and enforce channel usage.
17.5 Select statement
Section titled “17.5 Select statement”select {case v := <-ch1: fmt.Println(v)case ch2 <- 100: fmt.Println("sent")default: fmt.Println("no activity")}- Definition:
selectwaits on multiple channel operations. - What it solves: It allows multiplexing channels (like a channel‑based
switch). - When to use: When you need to handle multiple channels simultaneously (e.g., timeouts, cancellations).
Usage example:
ch1 := make(chan int)ch2 := make(chan int)go func() { ch1 <- 42 }()select {case v := <-ch1: fmt.Println("from ch1:", v)case <-ch2: fmt.Println("from ch2")}Output:
from ch1: 4217.6 Closing channels and range
Section titled “17.6 Closing channels and range”close(ch)for v := range ch { // reads until channel closed}- Definition:
closeindicates no more sends;rangereads until close. - What it solves: It signals completion to receivers.
- When to use: To tell a receiver that no more values are coming (e.g., producer‑consumer).
Usage example:
ch := make(chan int, 3)ch <- 1ch <- 2close(ch)for v := range ch { fmt.Println(v)}Output:
1217.7 WaitGroup
Section titled “17.7 WaitGroup”var wg sync.WaitGroupwg.Add(1)go func() { defer wg.Done() // work}()wg.Wait()- Definition: WaitGroup waits for a collection of goroutines to finish.
- What it solves: It provides a simple counter to synchronise completion.
- When to use: When you have multiple goroutines and need to wait for all to finish.
Usage example:
var wg sync.WaitGroupfor i := 0; i < 3; i++ { wg.Add(1) go func(i int) { defer wg.Done() fmt.Println(i) }(i)}wg.Wait()Output (order may vary):
20117.8 Mutex
Section titled “17.8 Mutex”var mu sync.Mutexmu.Lock()// critical sectionmu.Unlock()- Definition: A mutex provides mutual exclusion to protect shared data.
- What it solves: It prevents data races when multiple goroutines access shared memory.
- When to use: When you have shared variables that are written by multiple goroutines.
Usage example:
var mu sync.Mutexvar counter intfor i := 0; i < 1000; i++ { go func() { mu.Lock() counter++ mu.Unlock() }()}time.Sleep(100 * time.Millisecond)fmt.Println(counter)Output (approximately 1000):
1000→ Chapter 9.1: Goroutines
→ Chapter 9.2: Channels
→ Chapter 9.14: WaitGroups
→ Chapter 9.17: Mutexes
18. Additional Syntax Elements
Section titled “18. Additional Syntax Elements”18.1 new built‑in
Section titled “18.1 new built‑in”p := new(int) // p is *int, zeroed*p = 42- Definition:
newallocates zeroed memory for a type and returns a pointer. - What it solves: It provides a way to create a pointer to a zeroed value.
- When to use: When you need a pointer to a zero value, but
&T{}is more common.
Usage example:
p := new(int)fmt.Println(*p) // 0*p = 100fmt.Println(*p) // 100Output:
010018.2 make vs new
Section titled “18.2 make vs new”makeis only for slices, maps, channels; returns initialised (not zeroed) value.newworks for any type; returns zeroed pointer.
18.3 len, cap, append, copy
Section titled “18.3 len, cap, append, copy”s := []int{1, 2, 3}l := len(s) // 3c := cap(s) // 3s = append(s, 4)copy(s2, s) // copies elements- Definition: Built‑ins for slice operations.
- What they solve: They provide efficient slice management.
- When to use:
lenandcapfor length/capacity;appendto grow;copyto duplicate.
Usage example:
s1 := []int{1, 2}s2 := make([]int, 2)copy(s2, s1)fmt.Println(s2)Output:
[1 2]18.4 defer order (LIFO)
Section titled “18.4 defer order (LIFO)”defer fmt.Println("first")defer fmt.Println("second")- Definition: Deferred calls are executed in LIFO (last‑in‑first‑out) order.
- What it solves: It ensures resources are cleaned up in reverse order of acquisition.
- When to use: Natural when you defer multiple close/unlock calls.
Output:
secondfirst18.5 Fallthrough in switch
Section titled “18.5 Fallthrough in switch”switch 2 {case 1: fmt.Println("1")case 2: fmt.Println("2") fallthroughcase 3: fmt.Println("3")}- Definition:
fallthroughforces execution into the next case. - What it solves: It provides C‑style switch behaviour when needed.
- When to use: Rarely; only when you explicitly want to execute multiple consecutive cases.
Output:
2318.6 Blank identifier in multiple returns
Section titled “18.6 Blank identifier in multiple returns”_, err := os.Open("file.txt") // ignore the file handle- Definition:
_discards a return value. - What it solves: It silences compiler warnings about unused variables.
- When to use: When you need only some of the return values.
18.7 Label and goto
Section titled “18.7 Label and goto”loop:for i := 0; i < 10; i++ { if i == 5 { break loop }}- Definition: Labels mark a statement;
gotojumps to a label. - What it solves: Labels allow breaking/continuing outer loops;
gotocan simplify error handling. - When to use: Use labels for breaking nested loops;
gotosparingly (e.g., centralised cleanup).
Usage example (goto):
i := 0start:if i < 3 { fmt.Println(i) i++ goto start}Output:
01218.8 Type conversion vs assertion
Section titled “18.8 Type conversion vs assertion”- Conversion:
int(3.14)works between compatible types. - Assertion:
i.(string)works only on interface values.
18.9 Embedded interfaces
Section titled “18.9 Embedded interfaces”type Reader interface { Read(p []byte) (n int, err error) }type Writer interface { Write(p []byte) (n int, err error) }type ReadWriter interface { Reader Writer}- Definition: An interface can embed other interfaces.
- What it solves: It composes interfaces without repeating method signatures.
- When to use: When building larger interfaces from smaller ones (e.g.,
io.ReadWriter).
19. Summary Table of Key Syntax
Section titled “19. Summary Table of Key Syntax”| Concept | Syntax example |
|---|---|
| Package | package main |
| Import | import "fmt" |
| Variable (full) | var x int = 42 |
| Short variable | y := 10 |
| Constant | const Pi = 3.14 |
| Iota | const ( A = iota ) |
| Function | func add(a, b int) int { return a+b } |
| Multiple returns | func div(a,b int) (int, error) { ... } |
| Variadic | func sum(nums ...int) int { ... } |
| Method (value recv) | func (c Counter) Value() int { return c.value } |
| Method (pointer recv) | func (c *Counter) Inc() { c.value++ } |
| Unnamed receiver | func (Counter) Desc() string { ... } |
| Error creation | errors.New("msg") or fmt.Errorf("%s", msg) |
| Defer | defer f.Close() |
| Panic/recover | defer func() { if r := recover(); r != nil { ... } }() |
| Struct literal | p := Person{Name: "Alice", Age: 30} |
| Pointer | p := &x, *p |
| Array | arr := [3]int{1,2,3} |
| Slice | s := []int{1,2,3}; append(s, 4) |
| Map | m := map[string]int{"a":1} |
| Interface | type Greeter interface { Greet() string } |
| Empty interface | var x interface{} or var x any |
| Type assertion | v, ok := i.(string) |
| Type switch | switch v := i.(type) { ... } |
| Generic function | func Identity[T any](x T) T { return x } |
| Generic type | type Stack[T any] struct { items []T } |
| Goroutine | go myFunc() |
| Unbuffered channel | ch := make(chan int); ch <- 42; v := <-ch |
| Buffered channel | ch := make(chan int, 2) |
| Channel direction | func send(ch chan<- int) |
| Select | select { case v := <-ch: ... } |
| Close channel | close(ch) |
| Range over channel | for v := range ch { ... } |
| WaitGroup | wg.Add(1); defer wg.Done(); wg.Wait() |
| Mutex | mu.Lock(); defer mu.Unlock() |
| new | p := new(int) |
| make | m := make(map[string]int, 10) |
| len/cap | len(slice), cap(slice) |
| append/copy | s = append(s, 1); copy(dst, src) |
| fallthrough | case 2: fmt.Println("2"); fallthrough |
| Blank identifier | _, err := do() |
| goto / label | start: ... goto start |
This document covers the essential syntax you will use daily in Go, including detailed explanations of what each construct solves and when to use it. For deeper examples and projects, refer to the individual chapter files linked throughout. If a chapter is not yet written, the link serves as a placeholder for future content.