Golang Real-World (Part 5): Functions, Parameters, Return Values & Error Handling

Golang Backend Real-World (Part 5): Functions, Parameters, Return Values & Error Handling
Golang Backend Real-World (Part 5): Functions, Parameters, Return Values & Error Handling

Functions in Real-World Golang

Functions are one of the core building blocks of every Golang backend.
In real-world projects, functions are used everywhere: handlers, services, repositories, utilities, and background jobs.

This part will help you master:

  • Function declaration
  • Input parameters
  • Multiple return values
  • Error handling
  • Variadic functions
  • Named returns
  • Higher-order functions
  • Real-world backend examples

1. Function Declaration

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

You can shorten parameters with the same type:

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


2. Multiple Return Values (Very Common in Backend)

Golang supports multiple return values — extremely useful for error handling.

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("cannot divide by zero")
    }
    return a / b, nil
}

Usage:

result, err := divide(10, 2)
if err != nil {
    log.Println(err)
}


3. Error Handling Pattern (Idiomatic Go)

Golang does not use exceptions.

Instead, functions return errors explicitly:

func findUser(id int) (*User, error) {
    user, err := db.FindByID(id)
    if err != nil {
        return nil, err
    }
    return user, nil
}

Standard pattern:

if err != nil {
    return ..., err
}

This is exactly how real services & repositories are written.


4. Variadic Functions

Functions that accept unlimited parameters:

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

Usage:

total := sum(1, 2, 3, 4)


5. Named Return Values

Sometimes useful for service functions and utilities.

func compute(a, b int) (result int) {
    result = a + b
    return
}

However, use this carefully:
✔ Clean for small utilities
✖ Avoid for complex functions (readability issue)


6. Functions as Values (Higher-Order Functions)

You can pass functions around:

func apply(x int, fn func(int) int) int {
    return fn(x)
}

Usage:

square := func(n int) int { return n * n }
println(apply(5, square))  // Output: 25

This is powerful for middleware, callbacks, and reusable logic.


7. Real-World Example: Service Layer Function

This is how a typical service function looks in a clean-architecture Golang backend:

func (s *UserService) CreateUser(input CreateUserDTO) (*User, error) {
    if input.Email == "" {
        return nil, fmt.Errorf("email is required")
    }

    user := User{
        Name:  input.Name,
        Email: input.Email,
    }

    savedUser, err := s.repo.Create(user)
    if err != nil {
        return nil, err
    }

    return savedUser, nil
}

Patterns used here:

  • Input parameters (input)
  • Multiple return (*User, error)
  • Validation logic
  • Service → repository call
  • Error propagation

8. Real-World Example: Handler Function

Handlers are also regular functions:

func (h *UserHandler) CreateUser(c *gin.Context) {
    var dto CreateUserDTO

    if err := c.ShouldBindJSON(&dto); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    user, err := h.service.CreateUser(dto)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, user)
}

Demonstrates:

  • Parameter binding
  • Calling service functions
  • Error handling
  • JSON response

🎯 Summary

In this part, you learned:

✔ Function declaration
✔ Parameters & return types
✔ Multiple return values
✔ Real-world error handling
✔ Variadic functions
✔ Named returns
✔ Higher-order functions
✔ Service & handler examples

About Leaf 51 Articles
"Thành công nuôi dưỡng sự hoàn hảo. Sự hoàn hảo lại nuôi lớn thất bại. Chỉ có trí tưởng tượng mới tồn tại."