Skip to content
All essays
CraftMarch 16, 202412 min

Go Programming Language Basics

Master the fundamentals of Go programming language

Ü
Ümit Uz
Mobile & Full Stack Developer

Go is a statically typed, compiled programming language designed at Google. It's known for simplicity, concurrency support, and efficient compilation. Let's master the fundamentals of Go programming.

What Makes Go Special?

Go was designed to address common frustrations with existing languages while maintaining simplicity and efficiency.

Key Features

  • Simple syntax and fast compilation
  • Built-in concurrency with goroutines
  • Garbage collection
  • Static typing with interfaces
  • Standard library is comprehensive
  • Fast compile times and execution

Hello World

go
package main

import "fmt"

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

Every Go program starts with package main for executables.

Variables and Types

Go has a clean type system with various built-in types.

Variable Declaration

go
// Short declaration (most common)
name := "Alice"
age := 30

// Explicit declaration
var city string
city = "New York"

// Multiple variables
var x, y int = 1, 2

// With type inference
var a, b = 1, "hello"

Basic Types

go
// Numeric types
var i int = 42
var f float64 = 3.14

// Strings
var s string = "hello"

// Booleans
var b bool = true

// Arrays (fixed size)
var arr [5]int

// Slices (dynamic size)
var slice []int = []int{1, 2, 3}

Constants

go
const Pi = 3.14
const (
    StatusOK = 200
    StatusNotFound = 404
)

Control Structures

Go has minimal but powerful control structures.

If Statements

go
if x > 0 {
    fmt.Println("positive")
} else if x < 0 {
    fmt.Println("negative")
} else {
    fmt.Println("zero")
}

// With initialization
if err := doSomething(); err != nil {
    log.Fatal(err)
}

For Loops

Go only has for loops, but they're versatile:

go
// While loop style
i := 0
for i < 5 {
    fmt.Println(i)
    i++
}

// Traditional for loop
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// Infinite loop
for {
    // do something forever
}

// Range over slice
nums := []int{1, 2, 3}
for i, num := range nums {
    fmt.Printf("index: %d, value: %d\n", i, num)
}

Switch Statements

go
switch day {
case "Monday":
    fmt.Println("Weekday")
case "Saturday", "Sunday":
    fmt.Println("Weekend")
default:
    fmt.Println("Unknown")
}

// Type switch
switch v := i.(type) {
case int:
    fmt.Println("Integer")
case string:
    fmt.Println("String")
default:
    fmt.Println("Unknown type")
}

Functions

Functions are first-class citizens in Go.

Basic Functions

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

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

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

Variadic Functions

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

// Usage
total := sum(1, 2, 3, 4, 5)

Closures

go
func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
    }
}

add := adder()
add(1) // returns 1
add(2) // returns 3

Data Structures

Go provides several data structures for organizing data.

Structs

go
type Person struct {
    Name string
    Age  int
}

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

Methods

go
type Rectangle struct {
    Width, Height float64
}

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

// Pointer receiver for mutation
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

Interfaces

Interfaces are implicit in Go:

go
type Shape interface {
    Area() float64
}

func measure(s Shape) {
    fmt.Println(s.Area())
}

// Rectangle automatically implements Shape if it has Area()

Maps

go
// Create map
m := make(map[string]int)

// Set key
m["key"] = 42

// Get key
value := m["key"]

// Delete key
delete(m, "key")

// Check if key exists
value, ok := m["key"]
if ok {
    fmt.Println("Key exists")
}

// Range over map
for key, value := range m {
    fmt.Printf("%s: %d\n", key, value)
}

Pointers

Go has pointers but no pointer arithmetic:

go
func main() {
    i := 42
    p := &i         // point to i
    fmt.Println(*p) // read i through pointer
    *p = 21         // set i through pointer
}

Error Handling

Go uses explicit error handling rather than exceptions.

Basic Error Handling

go
file, err := os.Open("file.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

Custom Errors

go
import "errors"

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

Error Wrapping

go
func processFile(path string) error {
    file, err := os.Open(path)
    if err != nil {
        return fmt.Errorf("open file: %w", err)
    }
    defer file.Close()
    // process file
    return nil
}

Packages and Modules

Go organizes code into packages and modules.

Importing Packages

go
import (
    "fmt"
    "math/rand"
    "time"
)

Creating Packages

go
// In file mathutils.go
package mathutils

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

Exported Names

Capitalized names are exported:

go
func ExportedFunction() {}  // Exported
func privateFunction() {}   // Not exported

Goroutines (Preview)

Goroutines are lightweight threads:

go
func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world")
    say("hello")
}

Defer

Defer runs a function after the surrounding function returns.

go
func main() {
    defer fmt.Println("world")
    fmt.Println("hello")
}
// Output: hello then world

Commonly used for cleanup:

go
file, err := os.Open("file.txt")
if err != nil {
    return err
}
defer file.Close() // Always close, even on error

Best Practices

  1. 1Use short variable declaration (:=) when possible
  2. 2Handle errors explicitly
  3. 3Use defer for cleanup
  4. 4Keep functions small and focused
  5. 5Use interfaces for abstraction
  6. 6Leverage the standard library
  7. 7Follow Go conventions (gofmt)

Common Mistakes

Forgetting to Check Errors

go
// Don't do this:
file, _ := os.Open("file.txt")

// Do this instead:
file, err := os.Open("file.txt")
if err != nil {
    return err
}

Confusing Slices and Arrays

go
// Array (fixed size)
var arr [5]int

// Slice (dynamic size)
var slice []int

Not Using Defer for Cleanup

go
// Always use defer for resources
file, err := os.Open("file.txt")
if err != nil {
    return err
}
defer file.Close()

Conclusion

Go's simplicity makes it easy to learn while powerful enough for production use. Its clean syntax, strong standard library, and built-in concurrency support make it excellent for modern software development.

Master these basics, and you'll be ready to tackle more advanced Go features like goroutines, channels, and building real applications. Go's philosophy of simplicity and clarity will help you write maintainable, efficient code.

Practice writing Go programs, explore the standard library, and get comfortable with Go's approach to error handling and concurrency. The language's minimalism is its strength, allowing you to focus on solving problems rather than wrestling with complex syntax.

Enjoyed this essay? Share it.

Related essays

Next essay
Responsive Design Patterns 2025