Interfaces
Interfaces define behavior without prescribing implementation:
// Interface definition
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// Composition of interfaces
type ReadWriter interface {
Reader
Writer
}
// Implementing an interface
type File struct {
// ...
}
// File implements Reader interface
func (f *File) Read(p []byte) (n int, err error) {
// Implementation here
return len(p), nil
}
// File implements Writer interface
func (f *File) Write(p []byte) (n int, err error) {
// Implementation here
return len(p), nil
}
// Using an interface
func Copy(dst Writer, src Reader) error {
// Any type that implements these interfaces can be used
// ...
}
The Empty Interface
// Empty interface can hold any value
var i interface{}
i = 42
i = "hello"
i = struct{ Name string }{"Alice"}
// Type assertions
s, ok := i.(string)
if !ok {
fmt.Println("i is not a string")
}
// Type switches
switch v := i.(type) {
case int:
fmt.Println("i is an int:", v)
case string:
fmt.Println("i is a string:", v)
default:
fmt.Println("unknown type")
}
Project Structure & Modules
Go code is organized into packages and modules:
Packages
// File: math/calculation.go
package math
// Exported function (starts with uppercase)
func Add(a, b int) int {
return a + b
}
// Unexported function (starts with lowercase)
func multiply(a, b int) int {
return a * b
}
Importing Packages
package main
import (
"fmt"
"example.com/myproject/math"
)
func main() {
result := math.Add(5, 3)
fmt.Println(result) // 8
// math.multiply(5, 3) // Error: unexported
}
Go Modules
// Initialize a new module
$ go mod init example.com/myproject
// Add a dependency
$ go get github.com/pkg/errors
// Update dependencies
$ go get -u
// Tidy dependencies (add missing, remove unused)
$ go mod tidy
Testing
Go includes built-in support for testing:
Writing Tests
// File: math/calculation_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Errorf("Add(2, 3) = %d; want %d", got, want)
}
}
// Table-driven tests
func TestMultiply(t *testing.T) {
tests := []struct {
a, b, want int
}{
{2, 3, 6},
{-1, 5, -5},
{0, 10, 0},
}
for _, tc := range tests {
got := multiply(tc.a, tc.b)
if got != tc.want {
t.Errorf("multiply(%d, %d) = %d; want %d",
tc.a, tc.b, got, tc.want)
}
}
}
Running Tests
// Run all tests
$ go test ./...
// Run tests with verbose output
$ go test -v
// Run a specific test
$ go test -run TestAdd
// Run benchmark tests
$ go test -bench=.
// Check test coverage
$ go test -cover
Standard Library Highlights
Go has a rich standard library that covers most common needs:
fmt - Formatted I/O with functions like Printf
io - Basic I/O interfaces
os - Platform-independent OS functionality
strings - String manipulation functions
time - Time and duration functionality
net/http - HTTP client and server implementations
encoding/json - JSON encoding and decoding
sync - Synchronization primitives
context - Package for managing deadlines, cancellations, etc.
HTTP Server Example
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Println("Server starting on port 8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}