Go Functions Tutorial
Functions are fundamental building blocks in Go that encapsulate reusable code. They support parameters, return values, and can be passed around like other values, enabling powerful programming patterns and abstractions.
1. Function Basics
Functions in Go are declared with the func keyword and can have parameters and return values.
// Basic function
func greet(name string) {
fmt.Println("Hello,", name)
}
// Function with return value
func add(a, b int) int {
return a + b
}
// Multiple return values
func divide(a, b float64) (float64, error) {
if b == 0.0 {
return 0.0, errors.New("division by zero")
}
return a / b, nil
}
// Calling functions by it's function name
greet("Alice")
sum := add(5, 3)
result, err := divide(10.0, 2.0)
Key Characteristics:
- Parameters and return types are declared after the name
- Functions can return multiple values (commonly used for errors)
- Functions are first-class citizens (can be assigned to variables)
- Go supports variadic functions (accepting variable number of args)
Function Basics Quiz
How does Go handle multiple return values?
2. Advanced Features
Go provides several powerful function features for flexible programming.
// Named return values
func calc(a, b int) (sum int, product int) {
sum = a + b
product = a * b
return // naked return (uses named values)
}
// Variadic functions
func average(nums ...float64) float64 {
total := 0.0
for _, num := range nums {
total += num
}
return total / float64(len(nums))
}
// Function as a type
type Operation func(int, int) int
func compute(a, b int, op Operation) int {
return op(a, b)
}
// Anonymous functions
func() {
fmt.Println("Immediately invoked function")
}()
Advanced Capabilities:
- Named return values for documentation and clarity
- Variadic parameters with
...syntax - Function types for passing functions as arguments
- Anonymous functions (closures) for inline use
Advanced Features Quiz
What's the purpose of named return values?
3. Closures
Go functions can form closures, capturing variables from their surrounding scope.
// Simple closure
func counter() func() int {
i := 0
return func() int {
i++
return i
}
}
// Practical closure example
func makeGreeter(prefix string) func(string) {
return func(name string) {
fmt.Println(prefix, name)
}
}
// Using closures
next := counter()
fmt.Println(next()) // 1
fmt.Println(next()) // 2
greet := makeGreeter("Welcome")
greet("Alice") // Welcome Alice
Closure Characteristics:
- Functions can reference variables from enclosing scopes
- Captured variables persist between calls
- Useful for creating specialized functions
- Common in middleware and iterator patterns
Closures Quiz
What happens to closure variables between calls?
4. Defer, Panic, and Recover
Go provides special control flow mechanisms for functions.
// Defer statement
func readFile(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close() // executes when function exits
// Process file
return nil
}
// Panic and recover
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("runtime error: %v", r)
}
}()
if b == 0 {
panic("division by zero")
}
return a / b, nil
}
Control Flow Features:
deferschedules calls when the function exitspanicstops normal execution and begins panickingrecoverregains control of a panicking goroutine- Deferred functions run in LIFO order
Control Flow Quiz
When do deferred functions execute?
5. Methods and Receivers
Go methods are functions with a receiver that operate on types.
// Method with value receiver
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// Method with pointer receiver
func (c *Circle) Scale(factor float64) {
c.Radius *= factor
}
// Using methods
c := Circle{Radius: 5}
area := c.Area()
c.Scale(2)
// Non-struct receivers
type MyInt int
func (m MyInt) IsEven() bool {
return m%2 == 0
}
Method Features:
- Value receivers operate on copies (don't modify original)
- Pointer receivers can modify the original value
- Methods can be defined on any type in your package
- Go automatically converts between values and pointers when calling methods
Methods Quiz
When should you use a pointer receiver?
6. Common Function Patterns
Idiomatic Go uses several standard function patterns.
// Middleware pattern
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
// Constructor function
func NewUser(name string, age int) *User {
return &User{
Name: name,
Age: age,
}
}
// Option pattern for flexible configuration
type ServerOptions struct {
Port int
Timeout time.Duration
}
type Option func(*ServerOptions)
func WithPort(port int) Option {
return func(o *ServerOptions) {
o.Port = port
}
}
func NewServer(opts ...Option) *Server {
options := &ServerOptions{Port: 8080, Timeout: 30 * time.Second}
for _, opt := range opts {
opt(options)
}
// Create server with options
}
Practical Patterns:
- Middleware for wrapping functionality
- Constructor functions for initialization
- Option pattern for flexible configuration
- Higher-order functions for abstraction
- Worker pools using function channels
Patterns Quiz
What's the main benefit of the Option pattern?
7. Performance Tips
Understanding function performance characteristics helps write efficient Go code.
// Inlining small functions
func add1(x int) int { return x + 1 } // Likely inlined
// Avoiding heap allocations
func process(data []byte) {
// Work with slice (no copy if not escaped)
}
// Reducing interface conversions
type Processor interface {
Process()
}
type myProcessor struct{}
func (p myProcessor) Process() {}
func useProcessor(p Processor) {
p.Process()
}
// Using sync.Pool for heavy allocations
var bufferPool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(make([]byte, 0, 1024))
},
}
Optimization Tips:
- Small functions may be inlined by the compiler
- Minimize allocations in hot paths
- Be mindful of interface conversion costs
- Consider sync.Pool for frequently allocated objects
- Use benchmarks to validate optimizations
Performance Quiz
When might function inlining occur?