Go Functions
Go Functions
A function is a named, reusable block of code that performs a specific task. Functions let you organize a program into logical pieces, avoid repeating the same code in multiple places, and make programs easier to read, test, and maintain.
A function never runs on its own — it executes only when something explicitly calls it.
Why Use Functions?
- Promote code reuse — write the logic once, call it wherever it's needed.
- Improve program structure by breaking a large problem into smaller, named pieces.
- Simplify debugging — a bug is easier to isolate when logic lives in one clearly named place.
- Reduce duplication, which reduces the number of places a future change has to be made.
Defining a Function
func functionName() {
// code to execute
}
funcdeclares a function.- The function body sits inside
{}and runs top to bottom when the function is called.
Calling a Function
A function only runs when its name is followed by parentheses:
package main
import "fmt"
// function definition
func displayWelcome() {
fmt.Println("Welcome to Go programming!")
}
func main() {
displayWelcome() // function call
}
Expected output:
Welcome to Go programming!
Calling a Function Multiple Times
package main
import "fmt"
func printSeparator() {
fmt.Println("-----------")
}
func main() {
printSeparator()
fmt.Println("Section 1")
printSeparator()
fmt.Println("Section 2")
printSeparator()
}
Expected output:
-----------
Section 1
-----------
Section 2
-----------
Function Parameters and Arguments
Functions can accept input values, called parameters, which act as local variables inside the function body. This is what makes a function flexible and reusable instead of hard-coded to one specific value.
func FunctionName(param1 type, param2 type, param3 type) {
// code to execute
}
Parameters vs. arguments — a distinction worth being precise about:
- A parameter is the variable named in the function's declaration (e.g.,
nameinfunc greetUser(name string)). - An argument is the actual value supplied when the function is called (e.g.,
"Aarav"ingreetUser("Aarav")).
Example: Single Parameter
package main
import "fmt"
func greetUser(name string) {
fmt.Println("Welcome,", name+"!")
}
func main() {
greetUser("Aarav")
greetUser("Meera")
greetUser("Rohan")
}
Expected output:
Welcome, Aarav!
Welcome, Meera!
Welcome, Rohan!
Example: Multiple Parameters
package main
import "fmt"
func displayProfile(name string, age int) {
fmt.Println(name, "is", age, "years old.")
}
func main() {
displayProfile("Arjun", 21)
displayProfile("Sneha", 25)
displayProfile("Kiran", 30)
}
Expected output:
Arjun is 21 years old.
Sneha is 25 years old.
Kiran is 30 years old.
Tip: when consecutive parameters share the same type, Go lets you omit the type on all but the last: func displayProfile(name string, age int) can also be written with more parameters as func f(a, b string, c int).
Return Values
A function can send a value back to its caller. To do this, specify a return type in the function signature and use the return keyword.
func FunctionName(param1 type, param2 type) returnType {
// code to execute
return value
}
Example: Returning a Single Value
package main
import "fmt"
func multiply(a int, b int) int {
return a * b
}
func main() {
result := multiply(4, 5)
fmt.Println("Product:", result)
}
Expected output:
Product: 20
Named Return Values
Go allows you to name the return value directly in the function signature. Once named, that variable is automatically declared inside the function, and a bare return (with no value after it) returns whatever it currently holds:
package main
import "fmt"
func calculateTotal(price int, quantity int) (total int) {
total = price * quantity
return // returns the current value of 'total'
}
func main() {
fmt.Println(calculateTotal(150, 3))
}
An explicit return total is equally valid and, in most style guides, considered clearer for anything beyond a very short function — the implicit "naked" return is best reserved for small functions where the intent stays obvious at a glance.
Storing a Return Value
package main
import "fmt"
func square(n int) int {
return n * n
}
func main() {
value := square(6)
fmt.Println("Square:", value)
}
Multiple Return Values
One of Go's most distinctive features is that a function can return more than one value. This is used constantly throughout the standard library — most famously for returning a result and an error from the same call.
package main
import "fmt"
// Function returning multiple values
func getUserInfo(id int) (string, int) {
name := "User"
age := 20 + id
return name, age
}
func main() {
name, age := getUserInfo(2)
fmt.Println(name, age)
}
Expected output:
User 22
Ignoring Unwanted Return Values
If you don't need every value a function returns, discard the ones you don't want with the blank identifier _:
// Ignore the first value
_, age := getUserInfo(3)
fmt.Println("Age:", age)
// Ignore the second value
name, _ := getUserInfo(3)
fmt.Println("Name:", name)
Where this leads next: the two-value return pattern (
value, err := someFunction()) is the foundation of Go's approach to error handling, which you'll encounter throughout the standard library — for example,file, err := os.Open("data.txt"). This series covers error handling in a dedicated lesson once functions and structs are established.
Recursion
A function that calls itself is called recursive. Recursion breaks a problem down into smaller versions of the same problem, repeating until it reaches a base case — a condition simple enough to answer directly, without another recursive call.
Without a correctly defined base case, a recursive function calls itself indefinitely, which eventually crashes the program with a stack overflow. A well-formed recursive function always has two parts:
- Base case — stops the recursion.
- Recursive case — calls the function again, with input that moves it closer to the base case.
Example: Counting with Recursion
package main
import "fmt"
func printNumbers(n int) {
if n > 5 {
return // base case
}
fmt.Println(n)
printNumbers(n + 1) // recursive case
}
func main() {
printNumbers(1)
}
Expected output:
1
2
3
4
5
Example: Factorial
package main
import "fmt"
func factorial(n int) int {
if n == 0 {
return 1 // base case
}
return n * factorial(n-1) // recursive case
}
func main() {
result := factorial(5)
fmt.Println("Factorial:", result)
}
Expected output:
Factorial: 120
When to Use Recursion
Recursion is a natural fit for:
- Problems that break down into smaller versions of themselves (factorial, Fibonacci).
- Traversing trees and graphs, where each node's children are processed the same way as the node itself.
- Divide-and-conquer algorithms.
Common mistake: forgetting the base case, or writing a base case that the recursive calls never actually reach (for example, decrementing n when the base case checks for n == 0 but the input starts negative) — either mistake produces infinite recursion and a runtime crash rather than a clean error message.