Functions
## Learning Objectives
- Define and call functions
- Return multiple values
- Use variadic functions
- Understand closures
- Pass functions as values
- Use panic and recover
## Basic Functions
### Declaration
```go
func greet(name string) {
fmt.Println("Hello,", name)
}
func main() {
greet("Alice") // Hello, Alice
}
```
### With Return Value
```go
func add(a, b int) int {
return a + b
}
func main() {
result := add(3, 5)
fmt.Println(result) // 8
}
```
### Multiple Parameters
```go
func greet(firstName, lastName string) {
fmt.Printf("Hello, %s %s\n", firstName, lastName)
}
```
## Multiple Return Values
### Basic Multiple Returns
```go
func divide(a, b float64) (float64, float64) {
quotient := a / b
remainder := a - quotient*b
return quotient, remainder
}
func main() {
q, r := divide(10, 3)
fmt.Printf("Quotient: %.2f, Remainder: %.2f\n", q, r)
}
```
### Named Return Values
```go
func rectangleProperties(width, height float64) (area, perimeter float64) {
area = width * height
perimeter = 2 * (width + height)
return // Naked return
}
```
### Ignore Return Values
```go
quotient, _ := divide(10, 3) // Ignore remainder
```
## Error Handling
### Returning Errors
```go
func safeDivide(a, b float64) (float64, error)) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := safeDivide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}
```
### Custom Error Types
```go
type DivisionError struct {
Dividend float64
Divisor float64
}
func (e *DivisionError) Error() string {
return fmt.Sprintf("cannot divide %.2f by zero", e.Dividend)
}
func safeDivide(a, b float64) (float64, error) {
if b == 0 {
return 0, &DivisionError{Dividend: a, Divisor: b}
}
return a / b, nil
}
```
## Variadic Functions
### Basic Variadic
```go
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func main() {
fmt.Println(sum(1, 2, 3)) // 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
fmt.Println(sum()) // 0
}
```
### Spread Operator
```go
nums := []int{1, 2, 3, 4, 5}
fmt.Println(sum(nums...)) // 15
```
### Multiple Variadic
```go
func concat(separator string, values ...string) string {
result := ""
for i, v := range values {
if i > 0 {
result += separator
}
result += v
}
return result
}
func main() {
fmt.Println(concat("-", "a", "b", "c")) // a-b-c
}
```
## Closures
### Basic Closure
```go
func main() {
add := func(a, b int) int {
return a + b
}
fmt.Println(add(3, 5)) // 8
}
```
### Closure with State
```go
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
next := counter()
fmt.Println(next()) // 1
fmt.Println(next()) // 2
fmt.Println(next()) // 3
}
```
### Closure for Caching
```go
func memoize(f func(int) int) func(int) int {
cache := make(map[int]int)
return func(n int) int {
if result, ok := cache[n]; ok {
return result
}
result = f(n)
cache[n] = result
return result
}
}
```
## Functions as Values
### Pass Function as Argument
```go
func apply(nums []int, fn func(int) int) []int {
result := make([]int, len(nums))
for i, n := range nums {
result[i] = fn(n)
}
return result
}
func main() {
nums := []int{1, 2, 3, 4, 5}
doubled := apply(nums, func(n int) int { return n * 2 })
fmt.Println(doubled) // [2 4 6 8 10]
}
```
### Return Function
```go
func multiplier(factor int) func(int) int {
return func(n int) int {
return n * factor
}
}
func main() {
double := multiplier(2)
triple := multiplier(3)
fmt.Println(double(5)) // 10
fmt.Println(triple(5)) // 15
}
```
## Anonymous Functions
### IIFE (Immediately Invoked)
```go
result := func() int {
sum := 0
for i := 1; i <= 10; i++ {
sum += i
}
return sum
}()
fmt.Println(result) // 55
```
### Named Parameters
```go
func operate(a, b int, op func(int, int) int) int {
return op(a, b)
}
func main() {
result := operate(10, 5, func(x, y int) int { return x + y })
fmt.Println(result) // 15
}
```
## Recursion
### Basic Recursion
```go
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1)
}
func main() {
fmt.Println(factorial(5)) // 120
}
```
### Fibonacci Recursive
```go
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
```
## Methods
### Value Receiver
```go
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func main() {
r := Rectangle{Width: 10, Height: 5}
fmt.Println(r.Area()) // 50
}
```
### Pointer Receiver
```go
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
func main() {
r := Rectangle{Width: 10, Height: 5}
r.Scale(2)
fmt.Println(r.Width, r.Height) // 20 10
}
```
## panic and recover
### panic
```go
func criticalSection() {
defer fmt.Println("Cleanup")
panic("Something went wrong!")
fmt.Println("This won't print")
}
func main() {
criticalSection()
fmt.Println("This won't print either")
}
```
### recover
```go
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from:", r)
}
}()
panic("Something went wrong!")
}
func main() {
safeCall()
fmt.Println("Program continues...")
}
```
### When to Use panic
- Reserved for truly unrecoverable situations
- Programming errors (e.g., nil pointer dereference)
- Not for expected error conditions (use error returns)
## Summary
- Functions declared with `func` keyword
- Multiple return values for error handling
- Variadic functions accept variable arguments with `...`
- Closures are functions that capture their environment
- Functions are first-class citizens (can be passed as values)
- Methods are functions with receiver parameters
- panic/recover for exceptional situations, not normal errors
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →