Golang cheat sheet

Useful golang commands and constructs

Page content

Here’s basic Go program structure, error handling patterns and channels vs goroutines comparison.

bunnies around golang sign

Go Language Cheatsheet

Basic Syntax

Package Declaration

package main

Import Packages

import "fmt"
import (
    "fmt"
    "math"
)

Main Function

func main() {
    // Your code here
}

Variables and Types

Variable Declaration

var name string
var age int = 25
x := 10 // Short declaration

Basic Types

  • bool
  • string
  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64
  • float32, float64
  • complex64, complex128

Control Structures

If Statement

if x > 0 {
    // code
} else if x < 0 {
    // code
} else {
    // code
}

For Loop

for i := 0; i < 10; i++ {
    // code
}

Range Loop

for index, value := range collection {
    // code
}

Switch Statement

switch variable {
case value1:
    // code
case value2:
    // code
default:
    // code
}

Functions

Function Declaration

func functionName(param1 type1, param2 type2) returnType {
    // code
    return value
}

Multiple Return Values

func divideAndRemainder(x, y int) (int, int) {
    return x / y, x % y
}

Data Structures

Arrays

var numbers int
numbers := int{1, 2, 3, 4, 5}

Slices

slice := []int{1, 2, 3}
slice := make([]int, 3, 5)

Maps

m := make(map[string]int)
m["key"] = value

Structs

type Person struct {
    Name string
    Age  int
}
p := Person{Name: "Alice", Age: 30}

Methods

Method Declaration

func (r Rectangle) Area() float64 {
    return r.width * r.height
}

Interfaces

Interface Declaration

type Shape interface {
    Area() float64
}

Concurrency

Goroutines

go functionName()

Channels

ch := make(chan int)
ch <- value  // Send
value := <-ch  // Receive

Error Handling

Error Checking

if err != nil {
    // Handle error
}

Defer

defer file.Close()

Testing

Test Function

func TestFunction(t *testing.T) {
    // Test code
}

This cheatsheet covers the most essential Go language constructs and commands. It includes basic syntax, control structures, functions, data structures, methods, interfaces, concurrency primitives, and error handling. Remember that Go emphasizes simplicity and readability, so these constructs form the foundation for writing efficient and clear Go code.

Error handling in Go

Error handling in Go is straightforward and explicit, emphasizing clarity and robustness. Here are the key techniques for handling errors in Go:

  1. Return errors as values: Functions that can fail should return an error as their last return value. For example:
func Hello(name string) (string, error) {
    if name == "" {
        return "", errors.New("empty name")
    }
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message, nil
}
  1. Always check for errors: After calling a function that returns an error, immediately check if the error is non-nil. For example:
result, err := SomeFunction()
if err != nil {
    // Handle the error
    log.Fatal(err)
}
  1. Use error wrapping: When propagating errors up the call stack, wrap them to add context using fmt.Errorf() with the %w verb. For example:
f, err := os.Open(path)
if err != nil {
    return nil, fmt.Errorf("open failed: %w", err)
}
  1. Utilize defer for cleanup: Use defer to ensure resources are properly closed or cleaned up, even if an error occurs.

  2. Create custom error types: Implement the error interface for custom error types to provide more detailed error information.

  3. Use the errors package: Leverage functions like errors.New() to create simple error messages, and errors.Is() or errors.As() for error type checking and conversion.

  4. Avoid using panic: Reserve panic for truly unrecoverable situations. Normal error handling should use return values.

  5. Provide explicit error information: Make error messages clear and informative to aid in debugging and troubleshooting.

By following these practices, you can create robust Go programs that handle errors effectively and maintain code clarity.

Best Practices for Goroutines and Channels in Go

Efficient Use of Goroutines

  1. Avoid excessive goroutine creation: Spawn goroutines judiciously, considering the nature of the task and whether it benefits from parallel execution.

  2. Proper synchronization: Use synchronization mechanisms like channels or wait groups to manage goroutines effectively and prevent resource wastage.

  3. Consider task nature: Evaluate if a task truly benefits from concurrent execution before using goroutines.

Effective Channel Usage

  1. Choose appropriate channel type: Use unbuffered channels for synchronization and buffered channels when you need to decouple sending and receiving operations.

  2. Buffer capacity: When using buffered channels, carefully consider the buffer size to balance performance and resource usage.

  3. Close channels properly: Ensure channels are closed when no more data will be sent to prevent deadlocks and resource leaks.

Concurrency Patterns

  1. Worker pool pattern: Implement worker pools using goroutines and channels for efficient task distribution and result collection.

  2. Producer-consumer pattern: Use goroutines as producers and consumers, with channels facilitating data flow between them.

Error Handling and Resource Management

  1. Use defer for cleanup: Employ defer statements to ensure proper resource cleanup, even in the presence of errors.

  2. Handle panics: Implement recover() in long-running goroutines to prevent the entire program from crashing due to a panic in a single goroutine.

Communication and Synchronization

  1. Prefer channels over shared memory: Use channels for communication between goroutines to avoid race conditions and simplify synchronization.

  2. Use select for multiple channels: Employ the select statement to handle multiple channel operations concurrently.

Performance Considerations

  1. Limit concurrent operations: Use semaphores or worker pools to limit the number of concurrent operations and prevent resource exhaustion.

  2. Avoid premature optimization: Profile your code to identify bottlenecks before applying concurrency optimizations.

Testing and Debugging

  1. Use race detector: Regularly run your tests with the -race flag to detect data races.

  2. Write concurrent tests: Create tests that specifically exercise your concurrent code paths to ensure reliability.

By following these best practices, you can effectively leverage Go’s concurrency model, making your programs more efficient, maintainable, and less prone to common concurrency-related issues.

See other tech blog articles.