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
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
// 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
// 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
const Pi = 3.14
const (
StatusOK = 200
StatusNotFound = 404
)Control Structures
Go has minimal but powerful control structures.
If Statements
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:
// 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
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
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
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
// Usage
total := sum(1, 2, 3, 4, 5)Closures
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 3Data Structures
Go provides several data structures for organizing data.
Structs
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p.Name)
}Methods
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:
type Shape interface {
Area() float64
}
func measure(s Shape) {
fmt.Println(s.Area())
}
// Rectangle automatically implements Shape if it has Area()Maps
// 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:
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
file, err := os.Open("file.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()Custom Errors
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
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
import (
"fmt"
"math/rand"
"time"
)Creating Packages
// In file mathutils.go
package mathutils
func Add(a, b int) int {
return a + b
}Exported Names
Capitalized names are exported:
func ExportedFunction() {} // Exported
func privateFunction() {} // Not exportedGoroutines (Preview)
Goroutines are lightweight threads:
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.
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
// Output: hello then worldCommonly used for cleanup:
file, err := os.Open("file.txt")
if err != nil {
return err
}
defer file.Close() // Always close, even on errorBest Practices
- 1Use short variable declaration (:=) when possible
- 2Handle errors explicitly
- 3Use defer for cleanup
- 4Keep functions small and focused
- 5Use interfaces for abstraction
- 6Leverage the standard library
- 7Follow Go conventions (gofmt)
Common Mistakes
Forgetting to Check Errors
// 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
// Array (fixed size)
var arr [5]int
// Slice (dynamic size)
var slice []intNot Using Defer for Cleanup
// 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.