Chapter 44: Go Function Returns
Functions return values (especially multiple return values — the thing that makes Go feel so clean and safe compared to many other languages).
In Go, returning values is not just a detail — it’s a core part of the language’s philosophy:
- be explicit
- handle errors properly
- avoid exceptions / panics for normal control flow
- make code readable and maintainable
Let me explain everything about Go function returns like your personal teacher sitting next to you — slowly, clearly, with many copy-paste examples, real-world patterns, style notes, and the “why” behind each decision.
1. Basic Return – Single Value
The simplest form:
|
0 1 2 3 4 5 6 7 8 |
func add(a, b int) int { return a + b } |
|
0 1 2 3 4 5 6 7 8 9 |
func main() { result := add(17, 25) fmt.Println("17 + 25 =", result) // 42 } |
Key points:
- Return type is written after the parameter list
- You must return exactly one value of the declared type (or panic)
- return statement is mandatory unless the function has no return type
2. No Return Value (Void-like functions)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
func printGreeting(name string) { fmt.Printf("Namaste, %s from Hyderabad! 🇮🇳\n", name) } func main() { printGreeting("Webliance") // no return value expected } |
→ No return needed at the end (but you can write return early if you want to exit)
3. Multiple Return Values – The Go Superpower
This is the feature that makes Go feel different and better for real-world code.
|
0 1 2 3 4 5 6 7 8 9 10 11 |
func divide(a, b float64) (float64, error) { if b == 0 { return 0, fmt.Errorf("division by zero is not allowed") } return a / b, nil // nil means "no error" } |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
func main() { result, err := divide(100, 8) if err != nil { fmt.Println("Error:", err) } else { fmt.Printf("100 / 8 = %.2f\n", result) // 12.50 } // bad case result2, err2 := divide(100, 0) if err2 != nil { fmt.Println("Bad division →", err2) // prints the error } } |
Why multiple returns are so common in Go (especially with error):
- Go has no exceptions / no try-catch for normal errors
- Errors are values — you handle them explicitly
- Most functions that can fail return (something, error)
- Caller must check err != nil (compiler doesn’t force it — but community tools like golangci-lint can warn you)
4. Named Return Values – Very Idiomatic & Powerful
When you give names to return values, they are automatically declared as variables inside the function (zero-initialized) and you can use naked return (return without listing values).
|
0 1 2 3 4 5 6 7 8 9 10 |
func split(sum int) (x, y int) { x = sum * 4 / 9 // x is already declared y = sum - x return // naked return — sends current values of x and y } |
|
0 1 2 3 4 5 6 7 8 9 |
func main() { a, b := split(17) fmt.Println("Split 17 →", a, b) // 7 10 } |
Even more realistic example (very common pattern):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
func getUserStats(id int) (name string, age int, err error) { if id <= 0 { err = fmt.Errorf("invalid user id: %d", id) return // naked return — returns "", 0, err } // pretend database lookup name = "Webliance" age = 25 return // naked return → name, age, nil } |
Benefits of named returns:
- Signature becomes self-documenting ((name string, age int, err error))
- Naked return reduces repetition
- Very useful when you have early returns on error
Community advice 2025–2026:
- Use named returns when there are 2 or more return values
- Especially when one of them is error
- Avoid named returns for single-return functions (unnecessary)
5. Ignoring Return Values (Blank Identifier _)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
result, _ := divide(100, 8) // ignore the error fmt.Println(result) // or ignore result, only care about error _, err := divide(100, 0) if err != nil { fmt.Println("Failed:", err) } |
Rule: Go forbids unused variables — so if you don’t need a return value, use _
6. Quick Practice – Try Writing These
- Function minMax(numbers …int) (min, max int) that returns smallest & largest value
- Function getAreaAndPerimeter(length, width float64) (area, perimeter float64)
- Function parseInt(s string) (n int, err error) that tries strconv.Atoi
- Function login(username, password string) (success bool, message string)
Which one did you enjoy writing most?
Any part still confusing?
- Why Go prefers (value, error) instead of exceptions?
- When to use named returns vs plain returns?
- Multiple returns vs single return + struct?
- Or ready for pointers as return values / defer next?
Keep writing small functions with different return patterns — this is the moment when you go from “learning syntax” to “building real reusable logic”.
You’re progressing really fast — keep asking! 💪🇮🇳🚀
