Go

#12
TIOBE#7
PYPL#12
GitHub#20
RedMonk#12
IEEESpectrum#8
JetBrains#11
Programming LanguageSystems ProgrammingConcurrencyMicroservicesCloudDevOps

Programming Language

Go

Overview

Go is a simple and high-performance programming language developed by Google.

Details

Go was developed in 2009 by Robert Griesemer, Rob Pike, and Ken Thompson at Google as an open-source programming language. It features simple and readable syntax, fast compilation, and excellent concurrency capabilities. While having garbage collection, it achieves high performance and is suitable for large-scale system development. It's particularly popular for developing microservices, cloud applications, and DevOps tools, with important tools like Docker, Kubernetes, and Prometheus written in Go.

Code Examples

Hello World

package main

import "fmt"

// Basic output
func main() {
    fmt.Println("Hello, World!")
    
    // Output using variables
    message := "Hello, Go!"
    fmt.Println(message)
    
    // Output using format strings
    name := "John"
    age := 25
    fmt.Printf("My name is %s and I am %d years old.\n", name, age)
}

Variables and Types

package main

import "fmt"

func main() {
    // Basic types
    var name string = "John"
    var age int = 25
    var height float64 = 175.5
    var isActive bool = true
    
    // Short form variable declaration
    city := "Tokyo"
    temperature := 22.5
    
    // Multiple variable declaration
    var (
        firstName string = "John"
        lastName  string = "Doe"
        score     int    = 85
    )
    
    // Constants
    const Pi = 3.14159
    const MaxUsers = 100
    
    fmt.Printf("Name: %s, Age: %d, Height: %.1fcm\n", name, age, height)
    fmt.Printf("City: %s, Temperature: %.1f°C\n", city, temperature)
    fmt.Printf("Full name: %s %s, Score: %d\n", firstName, lastName, score)
    fmt.Printf("Pi: %g, Max users: %d\n", Pi, MaxUsers)
}

Arrays, Slices, and Maps

package main

import "fmt"

func main() {
    // Arrays (fixed length)
    var numbers [5]int = [5]int{1, 2, 3, 4, 5}
    fruits := [3]string{"apple", "banana", "orange"}
    
    // Slices (variable length)
    colors := []string{"red", "blue", "green"}
    colors = append(colors, "yellow") // Add element
    
    // Create slice with make function
    scores := make([]int, 3, 5) // length 3, capacity 5
    scores[0] = 85
    scores[1] = 92
    scores[2] = 78
    
    // Maps (dictionaries)
    person := map[string]interface{}{
        "name": "John Doe",
        "age":  30,
        "city": "Tokyo",
    }
    
    // Create map with make function
    grades := make(map[string]int)
    grades["Math"] = 85
    grades["English"] = 92
    grades["Science"] = 78
    
    fmt.Printf("Array: %v\n", numbers)
    fmt.Printf("Fruits: %v\n", fruits)
    fmt.Printf("Colors: %v\n", colors)
    fmt.Printf("Scores: %v\n", scores)
    fmt.Printf("Person: %v\n", person)
    fmt.Printf("Grades: %v\n", grades)
}

Functions

package main

import "fmt"

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

// Multiple return values
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("cannot divide by zero")
    }
    return a / b, nil
}

// Named return values
func rectangle(width, height float64) (area, perimeter float64) {
    area = width * height
    perimeter = 2 * (width + height)
    return // Named return values are automatically returned
}

// Variadic parameters
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

// Higher-order function (takes function as parameter)
func applyOperation(a, b int, operation func(int, int) int) int {
    return operation(a, b)
}

func main() {
    // Function calls
    result := add(10, 5)
    fmt.Printf("10 + 5 = %d\n", result)
    
    // Handle multiple return values
    quotient, err := divide(10, 3)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("10 ÷ 3 = %.2f\n", quotient)
    }
    
    // Named return values
    area, perimeter := rectangle(5, 3)
    fmt.Printf("Rectangle - Area: %.1f, Perimeter: %.1f\n", area, perimeter)
    
    // Variadic parameters
    total := sum(1, 2, 3, 4, 5)
    fmt.Printf("Sum: %d\n", total)
    
    // Higher-order function
    multiply := func(x, y int) int { return x * y }
    product := applyOperation(4, 6, multiply)
    fmt.Printf("4 × 6 = %d\n", product)
}

Structs and Methods

package main

import "fmt"

// Struct definition
type Person struct {
    Name string
    Age  int
    City string
}

// Method definition (function with receiver)
func (p Person) GetInfo() string {
    return fmt.Sprintf("%s (%d years old, lives in %s)", p.Name, p.Age, p.City)
}

// Pointer receiver (for modifying struct)
func (p *Person) SetAge(newAge int) {
    p.Age = newAge
}

// Embedding (inheritance-like feature)
type Employee struct {
    Person   // Embed Person
    JobTitle string
    Salary   int
}

func (e Employee) GetJobInfo() string {
    return fmt.Sprintf("%s - Job: %s, Salary: $%d", e.GetInfo(), e.JobTitle, e.Salary)
}

func main() {
    // Create struct instance
    person1 := Person{
        Name: "John Doe",
        Age:  25,
        City: "Tokyo",
    }
    
    // Access fields
    fmt.Println("Name:", person1.Name)
    fmt.Println("Age:", person1.Age)
    
    // Call method
    fmt.Println(person1.GetInfo())
    
    // Call pointer receiver method
    person1.SetAge(26)
    fmt.Println("Updated:", person1.GetInfo())
    
    // Embedded struct
    employee := Employee{
        Person: Person{
            Name: "Jane Smith",
            Age:  30,
            City: "Osaka",
        },
        JobTitle: "Engineer",
        Salary:   50000,
    }
    
    fmt.Println(employee.GetJobInfo())
}

Interfaces

package main

import "fmt"

// Interface definition
type Shape interface {
    Area() float64
    Perimeter() float64
}

// Rectangle struct
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// Circle struct
type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

// Function that accepts interface
func printShapeInfo(s Shape) {
    fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    rectangle := Rectangle{Width: 5, Height: 3}
    circle := Circle{Radius: 2}
    
    // Treat as same interface
    printShapeInfo(rectangle)
    printShapeInfo(circle)
    
    // Interface slice
    shapes := []Shape{rectangle, circle}
    for i, shape := range shapes {
        fmt.Printf("Shape %d: ", i+1)
        printShapeInfo(shape)
    }
}

Goroutines (Concurrency)

package main

import (
    "fmt"
    "sync"
    "time"
)

// Basic goroutine
func sayHello(name string) {
    for i := 0; i < 3; i++ {
        fmt.Printf("Hello %s! (%d)\n", name, i+1)
        time.Sleep(100 * time.Millisecond)
    }
}

// Synchronization with WaitGroup
func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // Call Done() when function exits
    
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    // Basic goroutines
    go sayHello("Alice")
    go sayHello("Bob")
    
    // Wait a bit in main goroutine
    time.Sleep(400 * time.Millisecond)
    fmt.Println("---")
    
    // Synchronized processing with WaitGroup
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1) // Increment counter
        go worker(i, &wg)
    }
    
    wg.Wait() // Wait for all goroutines to complete
    fmt.Println("All workers completed")
}

Channels (Communication in Concurrency)

package main

import (
    "fmt"
    "time"
)

// Basic channel
func sender(ch chan string) {
    messages := []string{"Hello", "Go", "Channels"}
    for _, msg := range messages {
        ch <- msg // Send to channel
        time.Sleep(100 * time.Millisecond)
    }
    close(ch) // Close channel
}

// Buffered channel
func bufferedChannelExample() {
    ch := make(chan int, 3) // Buffer size 3
    
    // Send data asynchronously
    go func() {
        for i := 1; i <= 5; i++ {
            ch <- i
            fmt.Printf("Sent: %d\n", i)
        }
        close(ch)
    }()
    
    // Receive data
    for value := range ch {
        fmt.Printf("Received: %d\n", value)
        time.Sleep(200 * time.Millisecond)
    }
}

// Select for controlling multiple channels
func selectExample() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(100 * time.Millisecond)
        ch1 <- "Channel 1"
    }()
    
    go func() {
        time.Sleep(200 * time.Millisecond)
        ch2 <- "Channel 2"
    }()
    
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("From ch1:", msg1)
        case msg2 := <-ch2:
            fmt.Println("From ch2:", msg2)
        case <-time.After(300 * time.Millisecond):
            fmt.Println("Timeout")
        }
    }
}

func main() {
    fmt.Println("=== Basic Channel ===")
    ch := make(chan string)
    go sender(ch)
    
    for message := range ch {
        fmt.Println("Received:", message)
    }
    
    fmt.Println("\n=== Buffered Channel ===")
    bufferedChannelExample()
    
    fmt.Println("\n=== Select Statement ===")
    selectExample()
}

Error Handling

package main

import (
    "errors"
    "fmt"
    "os"
)

// Custom error type
type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("Validation error in %s: %s", e.Field, e.Message)
}

// Function that returns error
func validateAge(age int) error {
    if age < 0 {
        return ValidationError{
            Field:   "age",
            Message: "age must be non-negative",
        }
    }
    if age > 150 {
        return ValidationError{
            Field:   "age",
            Message: "age must be 150 or less",
        }
    }
    return nil
}

// Error handling in file operations
func readFile(filename string) (string, error) {
    content, err := os.ReadFile(filename)
    if err != nil {
        return "", fmt.Errorf("file read error: %w", err)
    }
    return string(content), nil
}

// panic and recover
func riskyFunction() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Panic recovered: %v\n", r)
        }
    }()
    
    fmt.Println("Executing risky operation...")
    panic("something went wrong!")
    fmt.Println("This line won't be executed")
}

func main() {
    // Error handling examples
    ages := []int{25, -5, 200, 30}
    
    for _, age := range ages {
        if err := validateAge(age); err != nil {
            fmt.Printf("Error - Age %d: %v\n", age, err)
            
            // Type assertion to check custom error
            if ve, ok := err.(ValidationError); ok {
                fmt.Printf("  Field: %s\n", ve.Field)
            }
        } else {
            fmt.Printf("Age %d is valid\n", age)
        }
    }
    
    // File read error example
    _, err := readFile("nonexistent.txt")
    if err != nil {
        fmt.Printf("File error: %v\n", err)
    }
    
    // panic/recover example
    fmt.Println("\n=== Panic/Recover ===")
    riskyFunction()
    fmt.Println("Main processing continues")
}

Versions

Version Release Date Major Features
Go 1.23 2024-08 Range-over-func, Timer reset behavior
Go 1.22 2024-02 For-range over integers, Math/rand v2
Go 1.21 2023-08 Built-in functions min/max/clear, WASM
Go 1.20 2023-02 Comparable types, Context improvements
Go 1.19 2022-08 Generics improvements, Doc comments
Go 1.18 2022-03 Generics, Fuzzing, Workspaces
Go 1.17 2021-08 Module graph pruning, Call stack traces

Reference Links

Official Documentation

Learning Resources

Development Tools