Go Functions Basics — Junior Level¶
1. Introduction¶
What is it?¶
A function is a named, reusable block of code that takes inputs (parameters), does some work, and optionally returns one or more outputs. In Go, every program starts with a function called main, and functions are how you organize logic into pieces you can call from anywhere in your program.
How to use it?¶
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func main() {
sum := add(3, 4)
fmt.Println(sum) // 7
}
2. Prerequisites¶
- Basic Go syntax: variables, types,
fmt.Println - Knowledge of Go data types (int, string, bool)
- Understanding of
package mainand themain()entry point
3. Glossary¶
| Term | Definition |
|---|---|
func | The keyword that declares a function |
| function name | The identifier used to call the function (e.g., add) |
| parameter | A named input variable in the function declaration |
| argument | The actual value passed at the call site |
| return type | The type of value the function gives back |
| return statement | return expr — exits the function with a value |
| call | Invoking the function with name(args) |
| signature | The combination of parameter types and result types |
| void function | A function that returns nothing |
main | The entry point of any executable Go program |
init | A special function that runs before main |
| first-class value | A function that can be stored in variables, passed around, returned |
4. Core Concepts¶
4.1 Declaring a Function¶
The func keyword starts the declaration. After the name come parentheses with parameters, then the return type, then the body in braces.
4.2 Calling a Function¶
Use the function name followed by parentheses with arguments:
4.3 Functions With No Parameters or No Return¶
func sayHi() { // no params, no return
fmt.Println("Hi!")
}
func answer() int { // no params, returns int
return 42
}
func log(message string) { // takes input, returns nothing
fmt.Println("[LOG]", message)
}
4.4 Multiple Parameters¶
List each parameter with its type. If consecutive parameters share a type, you can group them.
func area(width float64, height float64) float64 {
return width * height
}
// Equivalent — grouped because both are float64:
func areaGrouped(width, height float64) float64 {
return width * height
}
4.5 The return Statement¶
A function with a return type must end with a return. The returned value's type must match the declared return type.
A function with no return type may omit return:
5. Real-World Analogies¶
A vending machine: You put in money (arguments), press a button (call the function), and a snack comes out (return value). The machine's internal mechanism is hidden — you only see inputs and outputs.
A recipe: A recipe says "given flour, sugar, and eggs, produce a cake." The recipe is the function declaration; cooking it once is the call.
A calculator button: Pressing √ on a calculator takes one input (the displayed number) and produces one output (its square root). Functions are reusable buttons.
6. Mental Models¶
┌─────────────────────────────┐
arguments │ │ return value
────────►│ func name(params) result │────────►
│ { ...body... } │
└─────────────────────────────┘
Black-box thinking: When you call len(s) you don't care HOW it counts — you just trust that you give it a string and get an int back. Functions let you treat code as black boxes once you've written them.
7. Pros & Cons¶
Pros¶
- Reusability: write once, call many times
- Readability: a named function is self-documenting
- Testability: small functions are easy to test in isolation
- Decomposition: large problems split into small named pieces
- Composition: functions can call other functions
Cons¶
- Excessive small functions can fragment logic and hurt readability
- Each call has a small overhead (negligible in 99% of code)
- Recursion depth is limited by goroutine stack size
8. Use Cases¶
- Computing a value from inputs (math, parsing, formatting)
- Performing a side effect (printing, writing to a file)
- Validating input
- Encapsulating a step of an algorithm
- Wrapping an external API call
- Implementing a callback for sorting, filtering, or iterating
- Defining a package's public interface (
Exportednames)
9. Code Examples¶
Example 1 — Simple Add¶
package main
import "fmt"
func add(a, b int) int {
return a + b
}
func main() {
fmt.Println(add(2, 3)) // 5
}
Example 2 — Greeting Builder¶
package main
import "fmt"
func greet(name string) string {
return "Hello, " + name + "!"
}
func main() {
fmt.Println(greet("Ada"))
fmt.Println(greet("Linus"))
}
Example 3 — Validating Input¶
package main
import "fmt"
func isAdult(age int) bool {
return age >= 18
}
func main() {
fmt.Println(isAdult(17)) // false
fmt.Println(isAdult(21)) // true
}
Example 4 — Computing a Value¶
package main
import "fmt"
func celsiusToFahrenheit(c float64) float64 {
return c*9/5 + 32
}
func main() {
fmt.Printf("%.1f°F\n", celsiusToFahrenheit(100)) // 212.0°F
}
Example 5 — Function With No Return¶
package main
import "fmt"
func banner(title string) {
fmt.Println("==================")
fmt.Println(" ", title)
fmt.Println("==================")
}
func main() {
banner("Welcome")
}
Example 6 — Calling a Function from Another Function¶
package main
import "fmt"
func square(x int) int {
return x * x
}
func sumOfSquares(a, b int) int {
return square(a) + square(b)
}
func main() {
fmt.Println(sumOfSquares(3, 4)) // 9 + 16 = 25
}
Example 7 — Function Stored in a Variable¶
package main
import "fmt"
func double(x int) int { return x * 2 }
func main() {
var op func(int) int = double
fmt.Println(op(5)) // 10
}
10. Coding Patterns¶
Pattern 1 — Helper Function¶
Pattern 2 — Predicate (returns bool)¶
Pattern 3 — Constructor¶
type User struct {
Name string
Age int
}
func newUser(name string, age int) User {
return User{Name: name, Age: age}
}
Pattern 4 — Pure Computation¶
A "pure" function depends only on its inputs and produces no side effects:
Pattern 5 — Wrapper¶
func logged(action string) {
fmt.Println(">>>", action)
// do the action
fmt.Println("<<<", action)
}
11. Clean Code Guidelines¶
- One function, one responsibility. If a function is doing two things, split it.
- Use clear, verb-based names:
calculateTotal,validateEmail, notdoorprocess. - Keep functions short — if a function does not fit on one screen, consider splitting.
- Prefer few parameters: 0–3 is ideal; 4+ suggests you should pass a struct instead.
- Avoid deep nesting: use early returns to flatten the body.
// Good — early return:
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// Bad — nested:
func divideBad(a, b float64) (float64, error) {
if b != 0 {
return a / b, nil
} else {
return 0, fmt.Errorf("division by zero")
}
}
12. Product Use / Feature Example¶
A checkout total calculator:
package main
import "fmt"
func itemTotal(price float64, quantity int) float64 {
return price * float64(quantity)
}
func applyDiscount(total, percent float64) float64 {
return total * (1 - percent/100)
}
func main() {
subtotal := itemTotal(19.99, 3)
final := applyDiscount(subtotal, 10) // 10% off
fmt.Printf("You pay: $%.2f\n", final)
}
Each function does one well-named thing and is trivially testable.
13. Error Handling¶
Junior idiom: if your function can fail, return (value, error).
package main
import (
"errors"
"fmt"
)
func safeDivide(a, b int) (int, 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)
}
(Multiple returns are covered in detail in 2.6.3.)
14. Security Considerations¶
- Validate inputs at the boundary: never trust caller-supplied strings/numbers without bounds checks.
- Never log sensitive parameters like passwords or tokens.
- Avoid panicking on bad input in library code; return an error instead.
- Use named functions for security-critical operations so they can be reviewed and tested.
func validatePort(p int) error {
if p < 1 || p > 65535 {
return fmt.Errorf("port out of range: %d", p)
}
return nil
}
15. Performance Tips¶
- Don't worry about call overhead for normal code — Go's compiler inlines small functions automatically.
- Avoid copying huge structs as parameters — pass a pointer (
*BigStruct) when the struct is large (>~64 bytes). - Reuse functions instead of duplicating logic — duplicate code is harder to optimize later.
- Avoid unnecessary allocations inside hot functions (covered in middle/senior levels).
// Less efficient for large structs:
func processBig(s BigStruct) { /* copies all fields */ }
// Better:
func processBig(s *BigStruct) { /* passes pointer (8 bytes) */ }
16. Metrics & Analytics¶
import "time"
func timed(name string, fn func()) {
start := time.Now()
fn()
fmt.Printf("[%s] took %v\n", name, time.Since(start))
}
func main() {
timed("expensive-op", func() {
// ... work ...
})
}
17. Best Practices¶
- Give functions clear, action-oriented names.
- Keep parameter lists short.
- Prefer return values over modifying caller state via pointers when possible.
- Document exported (capitalized) functions with a
// Name describes ...comment. - Make functions pure when feasible — easier to reason about and test.
- Always handle returned errors.
18. Edge Cases & Pitfalls¶
Pitfall 1 — Forgetting a return¶
Pitfall 2 — Wrong Return Type¶
func ageNextYear(age int) string {
return age + 1 // compile error: cannot use age + 1 (int) as string
}
Pitfall 3 — Calling a nil Function Variable¶
Pitfall 4 — Trying to Define a Nested Named Function¶
func outer() {
func inner() {} // compile error: nested func not allowed
}
// Use anonymous function instead:
func outer2() {
inner := func() {}
inner()
}
Pitfall 5 — Two Functions With the Same Name¶
func add(a, b int) int { return a + b }
func add(a, b string) string { return a + b } // compile error: add redeclared
19. Common Mistakes¶
| Mistake | Fix |
|---|---|
Forgetting return in a non-void function | Add return value at end of every code path |
| Mismatched return type | Match function signature exactly |
| Calling without enough arguments | Pass every required argument |
| Trying default values | Use option struct or extra function |
Calling a nil function variable | Initialize before calling |
Capitalization mistake (fmt.println vs fmt.Println) | Exported names start with uppercase |
20. Common Misconceptions¶
Misconception 1: "Functions in Go must return something." Truth: A function may have no return type — those are sometimes called void functions.
Misconception 2: "Go has function overloading like Java." Truth: Go does NOT support overloading. Each function name must be unique within a package.
Misconception 3: "I can give parameters default values." Truth: Go has no default parameter values. Use overload-by-name or option structs.
Misconception 4: "Functions can be defined inside other functions." Truth: Only anonymous functions (function literals) can; named functions must be at package level.
Misconception 5: "If I pass a slice to a function, it's a reference, so any change is visible to the caller." Truth: The slice header is passed by value, but the underlying array is shared. Modifying elements is visible; appending may or may not be visible. (See 2.7.3.)
21. Tricky Points¶
- The function name must be unique in the package (no overloading).
- Parameters are always passed by value — even slices and maps (their headers are copied).
- The return type goes after the parameter list, not before like in C/Java.
func main()takes NO arguments and returns NO values — useos.Argsfor CLI args.- A function with a result must have a
returnon every path — including insideif/switchbranches.
22. Test¶
package main
import "testing"
func add(a, b int) int { return a + b }
func TestAdd(t *testing.T) {
tests := []struct {
a, b, want int
}{
{1, 2, 3},
{-1, 1, 0},
{0, 0, 0},
{100, 200, 300},
}
for _, tt := range tests {
if got := add(tt.a, tt.b); got != tt.want {
t.Errorf("add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
}
}
}
23. Tricky Questions¶
Q1: What is the output?
A:5. The function does NOT modify n — it receives a copy. The result 10 is discarded. Q2: Will this compile?
A: No. Even though theif true branch is logically always taken, the compiler requires every code path to have a return. Q3: What is printed?
func choose() func() int {
return func() int { return 42 }
}
func main() {
fmt.Println(choose()())
}
42. choose() returns a function, and the second () calls it. 24. Cheat Sheet¶
// No params, no return
func ping() { }
// One param, one return
func double(x int) int { return x * 2 }
// Multiple params (grouped)
func add(a, b int) int { return a + b }
// Mixed param types
func tag(name string, count int) string { return "" }
// Multiple return values (covered in 2.6.3)
func divmod(a, b int) (int, int) { return a / b, a % b }
// Function value in a variable
var op func(int, int) int = add
// Calling
result := add(1, 2)
25. Self-Assessment Checklist¶
- I can declare a function with the
funckeyword - I understand parameters and arguments
- I can write a function that returns a value
- I can call a function and use its return value
- I know that
main()takes no arguments and returns nothing - I understand that Go has no default parameter values
- I know that nested named functions are not allowed
- I can store a function in a variable
- I can pass a function as an argument
- I know that arguments are passed by value
26. Summary¶
A Go function is declared with func name(params) returnType { body }. Functions are first-class values: you can store them in variables, pass them as arguments, and return them. Every parameter is passed by value (the function gets a copy). Each program needs exactly one func main() in package main. Go does not support function overloading or default parameter values. Use multiple return values when you need to return data plus an error.
27. What You Can Build¶
- Calculator functions (add, subtract, multiply, divide)
- Input validators (email, phone, range checks)
- Formatters and converters
- Small CLI utilities
- Reusable helper libraries
- Test fixtures and helpers
28. Further Reading¶
- Go Tour — Functions
- Effective Go — Functions
- Go Specification — Function declarations
- Go Blog — Errors are values
29. Related Topics¶
- 2.6.2 Variadic Functions
- 2.6.3 Multiple Return Values
- 2.6.4 Anonymous Functions
- 2.6.5 Closures
- 2.6.7 Call by Value
- 2.7 Pointers (for passing by reference)
- Chapter 3 Methods (functions with a receiver)
30. Diagrams & Visual Aids¶
Anatomy of a function¶
┌── keyword
│
│ ┌── name
│ │
│ │ ┌── parameter list (name + type)
│ │ │
│ │ │ ┌── return type
│ │ │ │
│ │ │ │ ┌── body
▼ ▼ ▼ ▼ ▼
func add(a, b int) int { return a + b }