Go Language Tutorial


Beginners To Experts


The site is under development.

Go Language Tutorial

1.1 What is Go?

Go (or Golang) is an open-source programming language developed by Google. It is statically typed, compiled, and designed for simplicity, efficiency, and high performance.

1.2 Installing Go

Download from the official website golang.org/dl and follow installation instructions for your OS.

1.3 Writing Your First Go Program

Let's write a simple program that prints "Hello, World!" to the console.

package main

import "fmt"

func main() {
fmt.Println("Hello, World!")
}

Explanation:

  • package main: Defines the package; main package tells Go this is an executable program.
  • import "fmt": Imports the "fmt" package, which contains functions for formatted I/O, including printing.
  • func main(): Defines the main function, entry point of the program.
  • fmt.Println("Hello, World!"): Prints the text to the console with a newline.
1.4 Running the Program

Save the file as hello.go, then run:

go run hello.go

This will compile and execute your Go program.

1.5 Variables and Types

Go is statically typed, so variables have types. Here's how to declare and use variables:

package main

import "fmt"

func main() {
var name string = "Alice" // Declare string variable
age := 30 // Short declaration with inferred type
fmt.Println("Name:", name)
fmt.Println("Age:", age)
}

Explanation:

  • var name string = "Alice": Declares a variable named name of type string.
  • age := 30: Short-hand for variable declaration and assignment; Go infers the type.
  • fmt.Println: Prints the variables with spaces between arguments.
1.6 Basic Data Types

Common Go types include:

  • int: Integer numbers
  • float64: Floating point numbers
  • string: Text
  • bool: Boolean true/false
1.7 Constants

Constants are immutable values:

const Pi = 3.14159
const Greeting = "Hello!"
1.8 Comments

Use // for single-line comments and /* ... */ for multi-line comments.

// This is a single-line comment
/*
This is a
multi-line comment
*/

2.1 If Statement

The if statement is used to run code conditionally based on a boolean expression.

package main

import "fmt"

func main() {
age := 20
if age >= 18 {
fmt.Println("You are an adult.")
}
}
2.2 If-Else Statement

if-else runs one block if the condition is true, another if false.

package main

import "fmt"

func main() {
age := 16
if age >= 18 {
fmt.Println("Adult")
} else {
fmt.Println("Minor")
}
}
2.3 Else If Ladder

Use multiple conditions with else if.

package main

import "fmt"

func main() {
score := 85
if score >= 90 {
fmt.Println("Grade A")
} else if score >= 75 {
fmt.Println("Grade B")
} else {
fmt.Println("Grade C")
}
}
2.4 Switch Statement Basic

Switch is an elegant way to replace multiple if-else checks.

package main

import "fmt"

func main() {
day := "Monday"
switch day {
case "Monday":
fmt.Println("Start of week")
case "Friday":
fmt.Println("End of week")
default:
fmt.Println("Midweek")
}
}
2.5 Switch with Multiple Cases

You can combine cases by separating values with commas.

package main

import "fmt"

func main() {
ch := 'a'
switch ch {
case 'a', 'e', 'i', 'o', 'u':
fmt.Println("Vowel")
default:
fmt.Println("Consonant")
}
}
2.6 Switch Without Expression

Switch can be used as an alternative to if-else by omitting the expression.

package main

import "fmt"

func main() {
num := 7
switch {
case num < 0:
fmt.Println("Negative")
case num == 0:
fmt.Println("Zero")
case num > 0:
fmt.Println("Positive")
}
}
2.7 For Loop Classic

The only loop type in Go: classic initialization; condition; post statement.

package main

import "fmt"

func main() {
for i := 1; i <= 5; i++ {
fmt.Println("Number:", i)
}
}
2.8 For Loop as While

For loop can be used like a while loop by omitting init and post.

package main

import "fmt"

func main() {
i := 1
for i <= 3 {
fmt.Println("Counting:", i)
i++
}
}
2.9 Infinite Loop

Loop with no condition runs forever unless broken.

package main

import "fmt"

func main() {
count := 0
for {
if count >= 3 {
break
}
fmt.Println("Loop", count)
count++
}
}
2.10 Loop Control: Continue and Break

continue skips the rest of current iteration, break exits loop.

package main

import "fmt"

func main() {
for i := 1; i <= 5; i++ {
if i == 3 {
continue
}
if i == 5 {
break
}
fmt.Println("Number:", i)
}
}

3.1 Defining a Function

Functions group reusable code blocks that can be called by name.

package main

import "fmt"

func greet() {
fmt.Println("Hello, World!")
}

func main() {
greet()
}
3.2 Function with Parameters

Functions can accept inputs called parameters.

package main

import "fmt"

func greet(name string) {
fmt.Println("Hello,", name)
}

func main() {
greet("Alice")
}
3.3 Function Returning a Value

Functions can return results using the return keyword.

package main

import "fmt"

func add(a, b int) int {
return a + b
}

func main() {
sum := add(3, 5)
fmt.Println("Sum:", sum)
}
3.4 Multiple Return Values

Go functions can return multiple values.

package main

import "fmt"

func swap(a, b string) (string, string) {
return b, a
}

func main() {
x, y := swap("first", "second")
fmt.Println(x, y)
}
3.5 Named Return Values

You can name return variables and omit them in the return statement.

package main

import "fmt"

func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}

func main() {
a, b := split(100)
fmt.Println(a, b)
}
3.6 Variadic Functions

Functions that accept variable number of arguments.

package main

import "fmt"

func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}

func main() {
fmt.Println(sum(1, 2, 3, 4))
}
3.7 Anonymous Functions

Functions without names, useful for inline use.

package main

import "fmt"

func main() {
f := func(msg string) {
fmt.Println(msg)
}
f("Hello from anonymous function")
}
3.8 Function as Arguments

Functions can be passed as parameters to other functions.

package main

import "fmt"

func apply(f func(int) int, x int) int {
return f(x)
}

func square(x int) int {
return x * x
}

func main() {
result := apply(square, 5)
fmt.Println(result)
}
3.9 Function Returning a Function

Functions can return other functions.

package main

import "fmt"

func multiplier(factor int) func(int) int {
return func(x int) int {
return x * factor
}
}

func main() {
double := multiplier(2)
fmt.Println(double(5))
}
3.10 Defer Statement

defer schedules a function call to run after the current function finishes.

package main

import "fmt"

func main() {
defer fmt.Println("World")
fmt.Println("Hello")
}

4.1 Arrays

Fixed-size collections of elements of the same type.

package main

import "fmt"

func main() {
var a [3]int = [3]int{1, 2, 3}
fmt.Println(a)
}
4.2 Array Length

Use len() to get the length of an array.

package main

import "fmt"

func main() {
a := [3]int{10, 20, 30}
fmt.Println("Length:", len(a))
}
4.3 Slices

Dynamic-sized, flexible view into arrays.

package main

import "fmt"

func main() {
s := []int{1, 2, 3}
fmt.Println(s)
}
4.4 Slicing Arrays

Extract portions of an array or slice.

package main

import "fmt"

func main() {
arr := [5]int{10, 20, 30, 40, 50}
slice := arr[1:4]
fmt.Println(slice)
}
4.5 Appending to a Slice

Use append() to add elements.

package main

import "fmt"

func main() {
s := []int{1, 2}
s = append(s, 3, 4)
fmt.Println(s)
}
4.6 Copying Slices

Use copy() to copy elements from one slice to another.

package main

import "fmt"

func main() {
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
fmt.Println(dst)
}
4.7 Maps

Key-value data structures.

package main

import "fmt"

func main() {
m := map[string]int{"a": 1, "b": 2}
fmt.Println(m)
}
4.8 Adding and Accessing Map Elements

Add and read map entries by key.

package main

import "fmt"

func main() {
m := make(map[string]int)
m["x"] = 10
fmt.Println(m["x"])
}
4.9 Deleting Map Elements

Use delete() to remove keys.

package main

import "fmt"

func main() {
m := map[string]int{"a": 1, "b": 2}
delete(m, "a")
fmt.Println(m)
}
4.10 Checking Map Keys

Check if a key exists.

package main

import "fmt"

func main() {
m := map[string]int{"a": 1}
val, ok := m["a"]
fmt.Println(val, ok) // 1 true
val2, ok2 := m["b"]
fmt.Println(val2, ok2) // 0 false
}

5.1 What is a Struct?

A struct is a composite data type grouping variables under one name.

package main

import "fmt"

type Person struct {
Name string
Age int
}

func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p)
}
5.2 Accessing Struct Fields

You can read or modify struct fields with dot notation.

package main

import "fmt"

type Person struct {
Name string
Age int
}

func main() {
p := Person{Name: "Bob", Age: 25}
fmt.Println(p.Name)
p.Age = 26
fmt.Println(p.Age)
}
5.3 Struct Literals

Create struct values using literals with or without field names.

package main

import "fmt"

type Point struct { X, Y int }

func main() {
p1 := Point{10, 20} // without field names
p2 := Point{X: 5, Y: 7} // with field names
fmt.Println(p1, p2)
}
5.4 Pointers to Structs

You can have pointers to structs and modify fields via pointers.

package main

import "fmt"

type Person struct { Name string }

func main() {
p := Person{Name: "Eve"}
ptr := &p
ptr.Name = "Eva"
fmt.Println(p.Name)
}
5.5 Structs as Function Parameters

Pass structs to functions by value or pointer.

package main

import "fmt"

type Person struct { Name string }

func updateName(p *Person, newName string) {
p.Name = newName
}

func main() {
p := Person{Name: "Sam"}
updateName(&p, "Samuel")
fmt.Println(p.Name)
}
5.6 What is an Interface?

An interface defines method signatures; types implement them implicitly.

package main

import "fmt"

type Speaker interface {
Speak() string
}

type Dog struct{}

func (d Dog) Speak() string {
return "Woof!"
}

func main() {
var s Speaker = Dog{}
fmt.Println(s.Speak())
}
5.7 Implementing Multiple Interfaces

A type can implement multiple interfaces by having required methods.

package main

import "fmt"

type Reader interface { Read() string }
type Writer interface { Write(string) }

type File struct { data string }

func (f *File) Read() string { return f.data }
func (f *File) Write(s string) { f.data = s }

func main() {
f := &File{}
var r Reader = f
var w Writer = f
w.Write("Hello")
fmt.Println(r.Read())
}
5.8 Empty Interface

The empty interface interface{} can hold values of any type.

package main

import "fmt"

func printValue(v interface{}) {
fmt.Println(v)
}

func main() {
printValue(42)
printValue("hello")
}
5.9 Type Assertion

Extract the dynamic type from an interface value using type assertion.

package main

import "fmt"

func main() {
var i interface{} = "test"
s, ok := i.(string)
if ok {
fmt.Println("String value:", s)
} else {
fmt.Println("Not a string")
}
}
5.10 Type Switch

Switch on the dynamic type of an interface value.

package main

import "fmt"

func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Println("Integer:", v)
case string:
fmt.Println("String:", v)
default:
fmt.Println("Other type")
}
}

func main() {
describe(10)
describe("hello")
describe(3.14)
}

6.1 Defining Methods

Methods are functions with a receiver argument.

package main

import "fmt"

type Circle struct { radius float64 }

func (c Circle) Area() float64 {
return 3.14 * c.radius * c.radius
}

func main() {
c := Circle{5}
fmt.Println("Area:", c.Area())
}
6.2 Pointer Receivers

Methods can have pointer receivers to modify the struct.

package main

import "fmt"

type Counter struct { count int }

func (c *Counter) Increment() {
c.count++
}

func main() {
c := Counter{}
c.Increment()
fmt.Println(c.count)
}
6.3 Method Value

You can assign a method to a variable and call it later.

package main

import "fmt"

type Person struct { Name string }

func (p Person) Greet() {
fmt.Println("Hi,", p.Name)
}

func main() {
p := Person{"Anna"}
greetFunc := p.Greet
greetFunc()
}
6.4 Method Expressions

Call a method by referencing the type explicitly.

package main

import "fmt"

type Person struct { Name string }

func (p Person) Greet() {
fmt.Println("Hello", p.Name)
}

func main() {
greet := Person.Greet
p := Person{"John"}
greet(p)
}
6.5 Embedding Structs

Struct embedding allows composition and promotes fields/methods.

package main

import "fmt"

type Animal struct { Name string }

func (a Animal) Speak() {
fmt.Println(a.Name, "makes a sound")
}

type Dog struct {
Animal
}

func main() {
d := Dog{Animal{Name: "Buddy"}}
d.Speak() // Dog inherits Speak method
}
6.6 Overriding Embedded Methods

You can override embedded methods by defining a method with the same name.

package main

import "fmt"

type Animal struct{}

func (a Animal) Speak() {
fmt.Println("Animal sound")
}

type Cat struct { Animal }

func (c Cat) Speak() {
fmt.Println("Meow")
}

func main() {
c := Cat{}
c.Speak() // Calls Cat's Speak, overrides Animal
}
6.7 Interfaces with Embedded Structs

Embedded structs can implement interfaces.

package main

import "fmt"

type Speaker interface { Speak() }

type Animal struct{}

func (a Animal) Speak() {
fmt.Println("Animal speaks")
}

type Bird struct { Animal }

func main() {
var s Speaker = Bird{}
s.Speak() // Calls embedded Animal's Speak
}
6.8 Method Sets and Interfaces

Only methods with matching receivers satisfy interfaces.

package main

import "fmt"

type Printer interface { Print() }

type Document struct{}

func (d *Document) Print() {
fmt.Println("Printing document")
}

func main() {
var p Printer
d := Document{}
// p = d // ERROR: Document value does not implement Printer
p = &d // OK: *Document implements Printer
p.Print()
}
6.9 Embedding Interfaces

Interfaces can embed other interfaces to compose behavior.

package main

import "fmt"

type Reader interface { Read() string }
type Writer interface { Write(string) }
type ReadWriter interface {
Reader
Writer
}

type File struct { data string }

func (f *File) Read() string { return f.data }
func (f *File) Write(s string) { f.data = s }

func main() {
var rw ReadWriter = &File{}
rw.Write("Hello")
fmt.Println(rw.Read())
}
6.10 Summary of Methods & Embedding

Methods add behavior to types; embedding promotes composition over inheritance.

7.1 What is Concurrency?

Concurrency allows multiple tasks to run independently to improve efficiency.

7.2 Goroutines

Goroutines are lightweight threads managed by Go runtime.

package main

import ("fmt"; "time")

func sayHello() {
fmt.Println("Hello from goroutine")
}

func main() {
go sayHello() // starts goroutine
time.Sleep(time.Second) // wait for goroutine
}
7.3 Channels

Channels allow communication and synchronization between goroutines.

package main

import "fmt"

func main() {
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
7.4 Buffered Channels

Buffered channels have capacity and don’t block immediately on send.

package main

import "fmt"

func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
7.5 Channel Direction

You can specify channels as send-only or receive-only for safety.

func send(ch chan<- string, msg string) { ch <- msg }
func receive(ch <-chan string) string { return <-ch }
7.6 Select Statement

Use select to wait on multiple channel operations.

package main

import "fmt"

func main() {
ch1 := make(chan string)
ch2 := make(chan string)

go func() { ch1 <- "one" }()
go func() { ch2 <- "two" }()

select {
case msg1 := <-ch1:
fmt.Println("Received", msg1)
case msg2 := <-ch2:
fmt.Println("Received", msg2)
}
}
7.7 Mutexes

Mutexes synchronize access to shared variables to avoid race conditions.

package main

import ("fmt"; "sync")

func main() {
var mu sync.Mutex
count := 0

mu.Lock()
count++
mu.Unlock()

fmt.Println(count)
}
7.8 WaitGroups

WaitGroups wait for a collection of goroutines to finish.

package main

import ("fmt"; "sync")

func main() {
var wg sync.WaitGroup
wg.Add(1)

go func() {
defer wg.Done()
fmt.Println("Goroutine finished")
}()

wg.Wait()
fmt.Println("All done")
}
7.9 Race Conditions

Unsynchronized access to shared variables causes race conditions.

// To detect: run with go run -race main.go
7.10 Summary of Concurrency

Goroutines and channels are the core of Go concurrency, enabling safe parallelism.

8.1 What is Error Handling?

Handling unexpected issues gracefully during program execution.

8.2 The error Type

Go uses the built-in error interface for errors.

package main

import ("errors"; "fmt")

func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}

func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
8.3 Custom Error Types

Create your own error types with more context.

package main

import ("fmt")

type MyError struct { msg string }

func (e *MyError) Error() string {
return e.msg
}

func doSomething(flag bool) error {
if !flag {
return &MyError{msg: "something went wrong"}
}
return nil
}

func main() {
err := doSomething(false)
if err != nil {
fmt.Println("Error:", err)
}
}
8.4 Wrapping Errors

Use fmt.Errorf with %w to wrap errors.

package main

import ("errors"; "fmt")

func readFile() error {
return errors.New("file not found")
}

func process() error {
err := readFile()
if err != nil {
return fmt.Errorf("process failed: %w", err)
}
return nil
}

func main() {
err := process()
if err != nil {
fmt.Println(err)
}
}
8.5 Checking Wrapped Errors

Use errors.Is and errors.As to inspect wrapped errors.

package main

import ("errors"; "fmt")

var ErrNotFound = errors.New("not found")

func find() error {
return fmt.Errorf("find error: %w", ErrNotFound)
}

func main() {
err := find()
if errors.Is(err, ErrNotFound) {
fmt.Println("Error is not found")
}
}
8.6 Panic and Recover

Use panic to stop execution and recover to handle panics.

package main

import "fmt"

func risky() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
panic("something bad happened")
}

func main() {
risky()
fmt.Println("Program continues")
}
8.7 Best Practices for Errors

Return errors, don't panic in normal flow; use errors to communicate problems.

8.8 Logging Errors

Use log package to record errors with timestamps.

package main

import ("log"; "os")

func main() {
f, err := os.Open("nonexistent.txt")
if err != nil {
log.Println("Error opening file:", err)
}
}
8.9 Returning Multiple Errors

Use packages or custom types to handle multiple errors if needed.

8.10 Summary of Error Handling

Handle errors explicitly and provide meaningful messages.

9.1 What are Packages?

Packages organize Go code into reusable units.

9.2 Creating a Package

Create a directory with Go files having the same package name.

// mathutil/mathutil.go
package mathutil

func Add(a, b int) int {
return a + b
}
9.3 Importing Packages

Use import statement to use other packages.

package main

import (
"fmt"
"yourmodule/mathutil"
)

func main() {
sum := mathutil.Add(2, 3)
fmt.Println("Sum:", sum)
}
9.4 Exported vs Unexported

Names starting with uppercase are exported and accessible outside the package.

9.5 Modules Introduction

Modules manage dependencies and versioning.

9.6 Creating a Module

Initialize a module with go mod init module_name.

9.7 Adding Dependencies

Use go get to add external packages.

9.8 go.mod File

Lists module path and dependencies.

module yourmodule

go 1.20

require github.com/pkg/errors v0.9.1
9.9 Vendor Directory

Optionally store dependencies locally with go mod vendor.

9.10 Summary of Packages and Modules

Packages structure code; modules handle dependency management for scalable projects.

10.1 Opening a File

Use os.Open to open an existing file for reading.

package main
import ("fmt"; "os")
func main() {
file, err := os.Open("test.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
fmt.Println("File opened successfully")
}
10.2 Creating a File

Use os.Create to create or truncate a file.

package main
import ("fmt"; "os")
func main() {
file, err := os.Create("newfile.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
fmt.Println("File created successfully")
}
10.3 Writing to a File

Use file.WriteString to write text to a file.

package main
import ("fmt"; "os")
func main() {
file, _ := os.Create("write.txt")
defer file.Close()
n, err := file.WriteString("Hello, Go!")
if err != nil {
fmt.Println("Error writing to file:", err)
return
}
fmt.Printf("%d bytes written\n", n)
}
10.4 Reading from a File

Use file.Read or utilities like io.ReadAll to read file content.

package main
import ("fmt"; "io"; "os")
func main() {
file, _ := os.Open("write.txt")
defer file.Close()
content, _ := io.ReadAll(file)
fmt.Println("File content:", string(content))
}
10.5 Closing Files

Always close files to release resources, often with defer file.Close().

// Shown in previous examples
10.6 Appending to a File

Open file with os.OpenFile using os.O_APPEND to add content.

package main
import ("fmt"; "os")
func main() {
file, _ := os.OpenFile("write.txt", os.O_APPEND|os.O_WRONLY, 0644)
defer file.Close()
file.WriteString("\nAppended line")
fmt.Println("Appended successfully")
}
10.7 File Permissions

Permissions like 0644 specify who can read/write files.

// Seen in os.Create("file.txt") and os.OpenFile(..., 0644)
10.8 File Info

Use file.Stat() to get file metadata.

package main
import ("fmt"; "os")
func main() {
file, _ := os.Open("write.txt")
defer file.Close()
info, _ := file.Stat()
fmt.Println("File name:", info.Name())
fmt.Println("Size:", info.Size())
fmt.Println("Mode:", info.Mode())
}
10.9 Deleting a File

Use os.Remove to delete a file.

package main
import ("fmt"; "os")
func main() {
err := os.Remove("newfile.txt")
if err != nil {
fmt.Println("Error deleting file:", err)
} else {
fmt.Println("File deleted successfully")
}
}
10.10 Summary

File handling is essential for data storage and retrieval in Go.

11.1 Defining Structs

Structs group related data into a single type.

package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p := Person{"Alice", 30}
fmt.Println(p.Name, p.Age)
}
11.2 Struct Methods

Define methods on structs to attach behavior.

func (p Person) Greet() {
fmt.Println("Hello,", p.Name)
}
11.3 Pointers to Structs

Use pointers to modify struct data in methods.

func (p *Person) HaveBirthday() {
p.Age++
}
11.4 Interfaces Introduction

Interfaces define method sets for types.

type Greeter interface {
Greet()
}
11.5 Implementing Interfaces

Types implement interfaces by having required methods.

func SayHello(g Greeter) {
g.Greet()
}
11.6 Empty Interface

interface{} can hold any type (like any).

var i interface{}
i = 42
i = "hello"
11.7 Type Assertions

Extract concrete value from interface.

if s, ok := i.(string); ok {
fmt.Println("String:", s)
}
11.8 Type Switches

Switch on type of interface value.

switch v := i.(type) {
case int:
fmt.Println("int", v)
case string:
fmt.Println("string", v)
}
11.9 Embedding Interfaces

Compose interfaces by embedding others.

type Reader interface {
Read(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Write(p []byte) (n int, err error)
}
11.10 Summary

Structs model data, interfaces define behavior, enabling flexible designs.

12.1 What are Go Modules?

Modules are the way Go manages dependencies and versions.

12.2 Initializing a Module
go mod init example.com/myapp

This creates a go.mod file for your project.

12.3 Adding Dependencies
go get github.com/sirupsen/logrus

This adds the package to your module dependencies.

12.4 go.mod File
module example.com/myapp

go 1.20

require github.com/sirupsen/logrus v1.8.1
12.5 go.sum File

Records checksums of your dependencies for security.

12.6 Updating Dependencies
go get -u github.com/sirupsen/logrus
12.7 Removing Unused Dependencies
go mod tidy
12.8 Using Replace Directive

Use in go.mod to replace a dependency for local testing.

replace github.com/pkg/errors => ../errors
12.9 Vendoring Dependencies
go mod vendor

Copies dependencies to a vendor folder.

12.10 Summary

Go modules simplify managing external packages and ensure reproducible builds.

13.1 What is an Error in Go?

In Go, errors are values returned from functions to indicate failure.

package main
import "fmt"
func divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
13.2 The `error` Type

The built-in `error` interface represents an error.

type error interface {
Error() string
}
13.3 Returning Errors from Functions

Functions can return multiple values, one being an error.

func sayHello(name string) (string, error) {
if name == "" {
return "", fmt.Errorf("name cannot be empty")
}
return "Hello, " + name, nil
}
13.4 Handling Errors in Main

Check the error returned before proceeding.

msg, err := sayHello("")
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println(msg)
}
13.5 Creating Custom Errors

Use errors.New() to create simple errors.

import "errors"
var ErrEmpty = errors.New("empty input")
func greet(name string) error {
if name == "" {
return ErrEmpty
}
fmt.Println("Hi,", name)
return nil
}
13.6 Using `fmt.Errorf()` with Formatting

Provides formatted error messages.

return fmt.Errorf("user %s not found", username)
13.7 Wrapping Errors (Go 1.13+)

Wrap underlying errors using `%w` verb in fmt.Errorf.

err := fmt.Errorf("operation failed: %w", ioErr)
13.8 Checking Error Types with `errors.Is`

Used for checking wrapped error types.

if errors.Is(err, os.ErrNotExist) {
fmt.Println("File does not exist")
}
13.9 Extracting Error Values with `errors.As`

Checks and unwraps specific error types.

var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("Path error:", pathErr.Path)
}
13.10 Summary

Go encourages explicit, simple error handling instead of exceptions.

14.1 What is Concurrency?

Concurrency allows multiple tasks to run simultaneously.

go sayHello() // Starts a goroutine
14.2 Goroutines

Lightweight threads managed by Go runtime.

func sayHello() { fmt.Println("Hello") }
go sayHello()
14.3 WaitGroups

Used to wait for a group of goroutines to finish.

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Task done")
}()
wg.Wait()
14.4 Channels

Used to communicate between goroutines.

ch := make(chan string)
go func() { ch <- "hello" }()
msg := <-ch
fmt.Println(msg)
14.5 Buffered Channels

Have capacity and don’t block immediately.

ch := make(chan int, 2)
ch <- 1
ch <- 2
14.6 Channel Direction

Limit sending or receiving direction.

func send(ch chan<- int) { ch <- 1 }
14.7 Select Statement

Waits on multiple channel operations.

select {
case msg := <-ch1:
fmt.Println(msg)
case msg := <-ch2:
fmt.Println(msg)
}
14.8 Mutex

Prevents race conditions using locks.

var mu sync.Mutex
mu.Lock()
// critical section
mu.Unlock()
14.9 Race Conditions

Occurs when goroutines access shared data.

Use go run -race to detect data races.
14.10 Summary

Go provides simple and powerful tools for concurrent programming.

15.1 What is a Struct?

Structs group related data together.

type Person struct {
Name string
Age int
}
15.2 Initializing Structs

Create an instance of a struct using field names.

p := Person{Name: "Alice", Age: 25}
15.3 Accessing Fields

Use dot notation to access or modify fields.

fmt.Println(p.Name)
p.Age = 30
15.4 Structs with Functions

Pass structs to functions.

func greet(p Person) {
fmt.Println("Hi", p.Name)
}
15.5 Anonymous Structs

Structs without a name, used for quick grouping.

emp := struct { Name string }{"John"}
15.6 Structs in Slices

Store multiple structs in slices.

people := []Person{{"A", 30}, {"B", 22}}
15.7 Nested Structs

Structs can contain other structs.

type Address struct { City string }
type User struct { Name string; Addr Address }
15.8 Comparing Structs

Structs are comparable if their fields are comparable.

p1 == p2 // returns true or false
15.9 Struct Tags

Used for JSON or other annotations.

type Product struct { ID int `json:"id"` }
15.10 Summary

Structs are powerful for modeling real-world entities.

16.1 What is an Interface?

Defines behavior through method signatures.

type Speaker interface { Speak() }
16.2 Implementing Interfaces

A type implements an interface by defining its methods.

type Person struct {}
func (p Person) Speak() { fmt.Println("Hello") }
16.3 Using Interfaces

Interfaces allow polymorphic behavior.

func saySomething(s Speaker) { s.Speak() }
16.4 Empty Interface

Represents any type, useful for generic behavior.

var x interface{} = 5
16.5 Type Assertion

Extracts the real value from an interface.

val := x.(int)
16.6 Type Switch

Handles multiple types using switch.

switch v := x.(type) {
case int: fmt.Println("Int", v)
case string: fmt.Println("String", v)
}
16.7 Interface Composition

Combining multiple interfaces.

type ReaderWriter interface { Reader; Writer }
16.8 Nil Interface Pitfall

Interface with nil underlying type is not nil.

var s Speaker
fmt.Println(s == nil) // true
16.9 Custom Interfaces

Create your own for modular design.

type Shape interface { Area() float64 }
16.10 Summary

Interfaces make Go flexible and extensible.

17.1 Errors in Go

Errors are values returned as the last result of a function.

err := errors.New("something went wrong")
17.2 Handling Errors

Check and handle errors immediately.

if err != nil { fmt.Println(err) }
17.3 Custom Error Types

Implement the error interface.

type MyError struct {}
func (e MyError) Error() string { return "my error" }
17.4 Wrapping Errors

Use fmt.Errorf to add context.

err := fmt.Errorf("read failed: %w", err)
17.5 Sentinel Errors

Predefined errors for comparison.

var ErrNotFound = errors.New("not found")
17.6 Error Is/As

Check specific error types.

errors.Is(err, ErrNotFound)
17.7 Panic and Recover

Handle serious errors using panic/recover.

defer func() {
if r := recover(); r != nil { fmt.Println("Recovered") }
}()
panic("fail")
17.8 Logging Errors

Use log package to log errors.

log.Println(err)
17.9 Ignoring Errors

Use `_` to ignore error (not recommended).

_, _ = strconv.Atoi("123")
17.10 Summary

Proper error handling is critical in Go programs.

18.1 Goroutine Basics

Goroutines are lightweight threads managed by Go.

go fmt.Println("Hello from goroutine")
18.2 Synchronization with WaitGroup

WaitGroup is used to wait for a group of goroutines to finish.

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Working")
}()
wg.Wait()
18.3 Using Channels

Channels are used for communication between goroutines.

ch := make(chan int)
go func() { ch <- 42 }()
fmt.Println(<-ch)
18.4 Buffered Channels

Buffered channels allow sending multiple values without immediate receive.

ch := make(chan int, 2)
ch <- 1
ch <- 2
18.5 Channel Direction

Channels can be restricted to send-only or receive-only.

func send(ch chan<- int) { ch <- 1 }
18.6 Select Statement

Waits on multiple channel operations.

select {
case val := <-ch1:
fmt.Println(val)
case val := <-ch2:
fmt.Println(val)
}
18.7 Closing Channels

Close a channel to signal no more values will be sent.

close(ch)
18.8 Range over Channel

Receive values until channel is closed.

for v := range ch { fmt.Println(v) }
18.9 Deadlocks

Avoid situations where goroutines are stuck waiting.

// Must ensure send and receive both happen
18.10 Summary

Concurrency is powerful but must be handled carefully.

19.1 What is a Go Module?

A collection of Go packages with versioning.

go mod init mymodule
19.2 go.mod File

Defines the module name and dependencies.

module mymodule
19.3 Adding Dependencies

Automatically updated when importing new packages.

import "github.com/some/module"
19.4 go.sum File

Verifies integrity of dependencies.

cat go.sum
19.5 Tidying Modules

Removes unused packages.

go mod tidy
19.6 Replacing Dependencies

Use `replace` directive in go.mod.

replace example.com/old => ../new
19.7 Requiring Specific Versions

Pin dependencies to a specific version.

require github.com/foo/bar v1.2.3
19.8 Upgrading Dependencies

Use `go get` to upgrade libraries.

go get -u github.com/foo/bar
19.9 Vendoring

Copy dependencies locally with `go mod vendor`.

go mod vendor
19.10 Summary

Go modules help manage project dependencies effectively.

20.1 Opening Files

Use os.Open to read files.

file, err := os.Open("data.txt")
20.2 Reading Files

Read file contents using ioutil or bufio.

data, _ := ioutil.ReadAll(file)
20.3 Writing to Files

Use os.Create and Write methods.

f, _ := os.Create("output.txt")
f.Write([]byte("Hello"))
20.4 Closing Files

Always close files to avoid resource leaks.

defer file.Close()
20.5 File Existence

Check if a file exists using os.Stat.

_, err := os.Stat("file.txt")
20.6 Deleting Files

Use os.Remove to delete files.

os.Remove("file.txt")
20.7 Appending to Files

Use os.OpenFile with append flag.

f, _ := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY, 0644)
20.8 File Permissions

Use os.Chmod to change permissions.

os.Chmod("file.txt", 0644)
20.9 Reading Line by Line

Use bufio.Scanner to read line by line.

scanner := bufio.NewScanner(file)
for scanner.Scan() { fmt.Println(scanner.Text()) }
20.10 Summary

Go provides comprehensive file handling utilities.

21.1 Introduction to Testing

Go uses the built-in testing package for unit testing.

import "testing"
func TestAdd(t *testing.T) {
  result := 2 + 2
  if result != 4 {
    t.Error("Expected 4")
  }
}
21.2 Writing Test Functions

Test function names must begin with Test.

func TestGreet(t *testing.T) {
  if greet() != "Hello" {
    t.Fail()
  }
}
21.3 Running Tests

Use go test to run tests in files ending with _test.go.

// Command line:
go test
21.4 Table-Driven Tests

Run tests over multiple inputs using a loop.

func TestSum(t *testing.T) {
  cases := []struct{ a, b, want int }{{1, 2, 3}, {2, 2, 4}}
  for _, c := range cases {
    got := c.a + c.b
    if got != c.want {
      t.Errorf("got %d, want %d", got, c.want)
    }
  }
}
21.5 Benchmark Tests

Measure performance using Benchmark functions.

func BenchmarkAdd(b *testing.B) {
  for i := 0; i < b.N; i++ {
    _ = 2 + 3
  }
}
21.6 Test Coverage

Measure which code paths were tested.

// Run with:
go test -cover
21.7 Setup and Teardown

Use TestMain for setup/teardown logic.

func TestMain(m *testing.M) {
  fmt.Println("Setup")
  code := m.Run()
  fmt.Println("Teardown")
  os.Exit(code)
}
21.8 Using Test Helpers

Extract common assertions to helper functions.

func assertEqual(t *testing.T, got, want int) {
  if got != want {
    t.Errorf("got %d, want %d", got, want)
  }
}
21.9 Mocking and Stubbing

Manually mock behaviors with interfaces and structs.

type DB interface { Get() string }
type mockDB struct{}
func (m mockDB) Get() string { return "mocked" }
21.10 Summary

Testing is essential for reliable and maintainable Go programs.

22.1 HTTP Package

Go provides the net/http package for building web apps.

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, "Hello, Web!")
})
http.ListenAndServe(":8080", nil)
22.2 Handling Routes

Register functions to respond to different URLs.

http.HandleFunc("/about", aboutHandler)
22.3 Request and Response

Read query and body data, write responses.

r.ParseForm()
name := r.FormValue("name")
fmt.Fprint(w, "Hello ", name)
22.4 Serving Static Files

Use FileServer to serve files from directories.

fs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
22.5 Custom Handlers

Implement ServeHTTP for full control.

type Hello struct{}
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, "Hello from custom handler")
}
22.6 URL Parameters

Parse parameters using query strings.

r.URL.Query().Get("id")
22.7 HTTP Methods

Check method types (GET, POST, etc).

if r.Method == http.MethodPost {
  // process POST
}
22.8 JSON APIs

Use json package to encode/decode data.

json.NewEncoder(w).Encode(data)
22.9 Middleware Basics

Wrap handlers for logging, auth, etc.

func logging(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    log.Println(r.URL.Path)
    next.ServeHTTP(w, r)
  })
}
22.10 Summary

Go makes web development simple and fast with net/http.

23.1 What is JSON?

JSON (JavaScript Object Notation) is a lightweight data interchange format, easy for humans to read and write and easy for machines to parse and generate.

23.2 Encoding JSON

Convert Go structs or data types into JSON using json.Marshal.

import "encoding/json"
type User struct {
  Name string
  Age  int
}
user := User{"Alice", 25}
data, err := json.Marshal(user)
if err != nil {
  panic(err)
}
fmt.Println(string(data))  // Output: {"Name":"Alice","Age":25}
23.3 Decoding JSON

Parse JSON data into Go structs using json.Unmarshal.

var user User
jsonStr := `{"Name":"Bob","Age":30}`
err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
  panic(err)
}
fmt.Println(user.Name, user.Age)  // Output: Bob 30
23.4 Using Maps with JSON

Decode JSON into generic maps for flexible handling.

var result map[string]interface{}
jsonStr := `{"Name":"Eve","Age":22}`
json.Unmarshal([]byte(jsonStr), &result)
fmt.Println(result["Name"])  // Output: Eve
23.5 JSON Struct Tags

Customize JSON key names using struct tags.

type User struct {
  Name string `json:"username"`
  Age  int    `json:"age"`
}
user := User{"Charlie", 28}
data, _ := json.Marshal(user)
fmt.Println(string(data))  // Output: {"username":"Charlie","age":28}
23.6 Omitting Empty Fields

Skip empty or zero-value fields in JSON using omitempty tag.

type User struct {
  Name    string `json:"name,omitempty"`
  Country string `json:"country,omitempty"`
}
user := User{Name: "Dana"}
data, _ := json.Marshal(user)
fmt.Println(string(data))  // Output: {"name":"Dana"}
23.7 Pretty Print JSON

Make JSON output more readable using json.MarshalIndent.

data, _ := json.MarshalIndent(user, "", "  ")
fmt.Println(string(data))
/*
Output:
{
  "name": "Dana"
}
*/
23.8 Streaming JSON with Decoder/Encoder

Process JSON streams efficiently using json.Decoder and json.Encoder.

decoder := json.NewDecoder(reader)
var user User
decoder.Decode(&user)
encoder := json.NewEncoder(writer)
encoder.Encode(user)
23.9 Handling JSON Errors

Always check for errors during JSON operations.

data, err := json.Marshal(user)
if err != nil {
  fmt.Println("Error encoding JSON:", err)
}
23.10 Summary

JSON handling is essential for data interchange in Go applications, offering powerful tools for encoding and decoding structured data.

24.1 Opening Files

Open existing files for reading using os.Open.

file, err := os.Open("example.txt")
if err != nil {
  panic(err)
}
defer file.Close()
24.2 Creating Files

Create or overwrite files using os.Create.

file, err := os.Create("output.txt")
if err != nil {
  panic(err)
}
defer file.Close()
24.3 Writing to Files

Write data to files using Write or WriteString.

_, err := file.WriteString("Hello, Go!")
if err != nil {
  panic(err)
}
24.4 Reading from Files

Read file contents using io/ioutil.ReadAll.

data, err := ioutil.ReadAll(file)
if err != nil {
  panic(err)
}
fmt.Println(string(data))
24.5 Using bufio for Buffered I/O

Use bufio.Reader for efficient line-by-line reading.

reader := bufio.NewReader(file)
line, err := reader.ReadString('\n')
if err != nil {
  panic(err)
}
fmt.Println(line)
24.6 File Info and Stat

Get metadata such as size and permissions using os.Stat.

info, err := os.Stat("example.txt")
if err != nil {
  panic(err)
}
fmt.Println("File size:", info.Size())
24.7 Closing Files

Always defer closing files to release resources.

defer file.Close()
24.8 Handling File Errors

Check for errors after every file operation to avoid unexpected behavior.

if err != nil {
  fmt.Println("File error:", err)
}
24.9 Renaming and Deleting Files

Use os.Rename and os.Remove to rename or delete files.

err := os.Rename("old.txt", "new.txt")
if err != nil {
  panic(err)
}
err = os.Remove("new.txt")
if err != nil {
  panic(err)
}
24.10 Summary

File handling in Go allows for flexible data management by supporting open, read, write, rename, and delete operations.

25.1 Database Drivers

Use Go SQL drivers to connect to databases like MySQL, PostgreSQL, SQLite.

import _ "github.com/go-sql-driver/mysql"
25.2 Opening a Database Connection

Connect to database using sql.Open.

db, err := sql.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/dbname")
if err != nil { panic(err) }
defer db.Close()
25.3 Ping Database

Check connection with db.Ping().

if err := db.Ping(); err != nil { panic(err) }
25.4 Executing Queries

Run SQL commands with db.Exec.

res, err := db.Exec("INSERT INTO users(name) VALUES(?)", "Alice")
if err != nil { panic(err) }
25.5 Querying Data

Fetch rows using db.Query or db.QueryRow.

row := db.QueryRow("SELECT name FROM users WHERE id = ?", 1)
var name string
err := row.Scan(&name)
if err != nil { panic(err) }
fmt.Println(name)
25.6 Using Prepared Statements

Prepare statements to execute multiple times safely.

stmt, err := db.Prepare("INSERT INTO users(name) VALUES(?)")
if err != nil { panic(err) }
defer stmt.Close()
_, err = stmt.Exec("Bob")
25.7 Transactions

Execute multiple queries atomically with transactions.

tx, err := db.Begin()
if err != nil { panic(err) }
_, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = ?", 1)
if err != nil { tx.Rollback(); panic(err) }
tx.Commit()
25.8 Closing Rows

Always close rows to free resources.

rows, err := db.Query("SELECT * FROM users")
if err != nil { panic(err) }
defer rows.Close()
25.9 Error Handling

Check errors after all DB operations.

if err != nil { fmt.Println("DB error:", err) }
25.10 Summary

Go's database/sql package provides a flexible way to work with databases using drivers, queries, transactions, and prepared statements.

26.1 Writing Test Functions

Test functions start with Test and take *testing.T.

func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Error("Expected 5")
}
}
26.2 Running Tests

Run tests with go test command in terminal.

26.3 Using Table-Driven Tests

Test multiple cases in a loop for clean code.

tests := []struct{ a, b, want int }{
{1, 2, 3},
{2, 2, 4},
}
for _, tt := range tests {
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d,%d) = %d; want %d", tt.a, tt.b, got, tt.want)
}
}
26.4 Benchmarks

Write benchmarks using func BenchmarkXxx(b *testing.B).

func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
26.5 Test Coverage

Check coverage with go test -cover.

26.6 Using Test Helpers

Create helper functions to reduce repetition in tests.

26.7 Mocking Dependencies

Use interfaces and mocks to isolate tests.

26.8 Table Driven Benchmarks

Benchmark multiple cases using table-driven approach.

26.9 Error Reporting

Use t.Fatal to stop tests on fatal errors.

26.10 Summary

Testing in Go is simple and powerful, promoting good practices and robust code.

27.1 Compiling for Production

Build optimized binaries using go build -ldflags="-s -w".

go build -ldflags="-s -w" main.go
27.2 Cross-Compilation

Compile binaries for different platforms.

GOOS=linux GOARCH=amd64 go build -o app-linux main.go
27.3 Using Environment Variables

Configure app settings securely using env vars.

import "os"
port := os.Getenv("PORT")
27.4 Dockerizing Go Applications

Create lightweight containers for easy deployment.

FROM golang:1.20-alpine
WORKDIR /app
COPY . .
RUN go build -o myapp .
CMD ["./myapp"]
27.5 Managing Configuration Files

Use config files with libraries like viper.

27.6 Logging in Production

Use structured logging with levels.

log.SetFlags(log.LstdFlags | log.Lshortfile)
27.7 Handling Signals

Gracefully shutdown on OS signals.

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
fmt.Println("Shutting down")
27.8 Monitoring and Metrics

Use tools like Prometheus for monitoring.

27.9 Using a Process Manager

Manage your Go app with tools like systemd or supervisord.

27.10 Summary

Proper deployment practices ensure your Go app is reliable, efficient, and maintainable.

28.1 Avoid Hardcoding Secrets

Never store passwords or keys in code. Use environment variables.

password := os.Getenv("DB_PASSWORD")
28.2 Validate User Input

Always sanitize and validate inputs to avoid injection.

if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(username) {
return errors.New("Invalid username")
}
28.3 Use HTTPS

Serve your app over TLS to encrypt data.

http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
28.4 Secure Cookies

Set cookies with Secure and HttpOnly flags.

cookie := &http.Cookie{
Name: "session",
Value: "xyz",
Secure: true,
HttpOnly: true,
}
http.SetCookie(w, cookie)
28.5 Protect Against CSRF

Use tokens to prevent Cross-Site Request Forgery.

// Example: Generate and validate CSRF token in forms
28.6 Use Prepared Statements

Prevent SQL injection by using prepared statements.

stmt, _ := db.Prepare("SELECT * FROM users WHERE id = ?")
row := stmt.QueryRow(userID)
28.7 Handle Errors Securely

Don’t expose internal error details to users.

if err != nil {
log.Println(err)
http.Error(w, "Internal Server Error", 500)
}
28.8 Limit Resource Usage

Set timeouts and limits to prevent DoS attacks.

srv := &http.Server{
Addr: ":8080",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
28.9 Regularly Update Dependencies

Keep your packages up to date to avoid vulnerabilities.

go get -u ./...
28.10 Summary

Applying security best practices ensures your Go apps remain safe and robust.

29.1 Profiling with pprof

Use pprof to analyze CPU and memory usage.

import _ "net/http/pprof"
go http.ListenAndServe(":6060", nil)
29.2 Benchmarking Code

Write benchmarks with testing.B.

func BenchmarkFib(b *testing.B) {
for i := 0; i < b.N; i++ {
Fib(10)
}
}
29.3 Avoiding Memory Leaks

Release unused memory and close resources.

defer file.Close()
29.4 Efficient String Handling

Use strings.Builder to concatenate strings efficiently.

var b strings.Builder
b.WriteString("Hello")
b.WriteString(" World")
result := b.String()
29.5 Use Goroutines Wisely

Don’t create excessive goroutines to avoid overhead.

go func() { /* work */ }()
29.6 Use Sync.Pool for Object Reuse

Reuse frequently allocated objects to reduce GC pressure.

var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
29.7 Minimize Lock Contention

Design concurrent code to reduce locks.

29.8 Use Efficient Data Structures

Choose the right data structure for your problem.

29.9 Inline Functions

Small functions are often inlined automatically for speed.

29.10 Summary

Profiling and optimization improve your Go app's speed and resource usage.

30.1 What is Internationalization?

Preparing your app to support multiple languages.

30.2 Using golang.org/x/text Package

Use this package for language and locale support.

import "golang.org/x/text/language"
30.3 Setting Language Preferences

Detect user language preference via HTTP headers.

lang := r.Header.Get("Accept-Language")
30.4 Message Translation Files

Store translations in JSON or YAML files.

30.5 Loading Translations

Load translation files at startup.

30.6 Using Printer for Formatting

Format messages for a given language.

p := message.NewPrinter(language.English)
p.Printf("Hello, %s!", "John")
30.7 Template Localization

Inject localized strings into templates.

30.8 Pluralization Support

Handle plurals correctly per language rules.

30.9 Timezone and Date Formatting

Format dates and times per locale.

30.10 Summary

Internationalization makes your Go apps usable worldwide.