In this lesson we cover the Go functions tutorial fundamentals — how to declare functions, return multiple values, use named return parameters, accept a variable number of arguments, and schedule cleanup with defer. If you completed Lesson 5 – Control Flow, you can branch and loop; functions let you package that logic into reusable, testable units.
Prerequisites: Lessons 1–5, especially Variables and Types and Control Flow. Estimated time: 45–55 minutes.
1. Function Declarations
A function is declared with the func keyword, a name, parameters in parentheses, and an optional return type:
package main
import "fmt"
func greet(name string) {
fmt.Printf("Hello, %s!\n", name)
}
func add(a int, b int) int {
return a + b
}
func main() {
greet("Kindson")
fmt.Println("3 + 4 =", add(3, 4))
}
When consecutive parameters share the same type, you can write a, b int instead of a int, b int. Functions are first-class values in Go — you can assign them to variables and pass them as arguments — but start with named functions at package level while learning. Names starting with an uppercase letter are exported (visible outside the package); lowercase names are private.
2. Multiple Return Values
Go functions can return more than one value. This pattern is central to error handling — a result plus an error:
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}
Callers must handle every return value or explicitly discard with _. Ignoring an error return is allowed by the compiler but is poor practice in production code. This explicit style differs from exceptions in languages like Java or Python — errors are values you check at the call site.
3. Named Return Values
You can name return parameters in the function signature. They act as variables inside the function, and a bare return statement returns their current values:
package main
import "fmt"
func rectangleStats(width, height float64) (area, perimeter float64) {
area = width * height
perimeter = 2 * (width + height)
return // naked return — returns area and perimeter
}
func main() {
a, p := rectangleStats(5, 3)
fmt.Printf("Area: %.1f, Perimeter: %.1f\n", a, p)
}
Named returns improve readability in short functions with obvious results, but overuse in long functions can obscure what is being returned. The Go community often reserves naked returns for very small functions — defer and error cleanup patterns are a common exception you will see in production code.
4. Variadic Functions
A variadic function accepts zero or more arguments of a specified type. The parameter is declared with an ellipsis before the type and arrives inside the function as a slice:
package main
import "fmt"
func sum(numbers ...int) int {
total := 0
for _, n := range numbers {
total += n
}
return total
}
func main() {
fmt.Println(sum(1, 2, 3)) // 6
fmt.Println(sum(10, 20)) // 30
fmt.Println(sum()) // 0
vals := []int{4, 5, 6}
fmt.Println(sum(vals...)) // spread a slice
}
Only the last parameter in a function can be variadic. Built-in examples include fmt.Println and append. Use variadic functions when the number of arguments is naturally variable — logging, formatting, or aggregating values — rather than forcing callers to always build a slice manually.
5. Introduction to defer
The defer statement schedules a function call to run when the surrounding function returns — regardless of how it returns (normal return, early return, or panic):
package main
import "fmt"
func process() {
fmt.Println("Start")
defer fmt.Println("Deferred cleanup")
fmt.Println("Middle")
fmt.Println("End")
}
func main() {
process()
}
// Output: Start, Middle, End, Deferred cleanup
Deferred calls run in LIFO order (last deferred, first executed). Typical uses: closing files, unlocking mutexes, and rolling back transactions. Pair defer file.Close() immediately after a successful os.Open so cleanup is never forgotten on an error path — a pattern you will use constantly in Go.
6. Putting Functions Together
Real Go code combines these features. Here is a small utility that demonstrates parameters, multiple returns, and defer:
package main
import (
"fmt"
"strings"
)
func formatLines(prefix string, lines ...string) (string, int) {
defer func() { fmt.Println("formatLines finished") }()
var builder strings.Builder
for _, line := range lines {
builder.WriteString(prefix)
builder.WriteString(line)
builder.WriteString("\n")
}
return builder.String(), len(lines)
}
func main() {
output, count := formatLines("> ", "First", "Second", "Third")
fmt.Print(output)
fmt.Println("Lines formatted:", count)
}
Compare this to organizing code in Python with def — Go requires explicit types, supports multiple returns natively, and uses defer instead of try/finally for cleanup. Both approaches encourage small, focused functions; Go’s type system catches mismatches at compile time.
7. Quick Reference
| Feature | Syntax | Typical Use |
|---|---|---|
| Basic function | func name(p T) R |
Reusable logic with typed input/output |
| Multiple returns | func f() (T, error) |
Result plus error checking |
| Named returns | func f() (x int, err error) |
Short functions, naked return |
| Variadic | func f(args ...T) |
Variable argument count |
| defer | defer cleanup() |
Run on function exit |
Exported functions (capitalized names) form the public API of a package. Keep functions small enough to read in one screen — if a function grows past roughly forty lines, consider splitting it.
8. Next Steps
You now understand the go functions tutorial core: declarations, multiple return values, named returns, variadic parameters, and defer. Upcoming lessons cover arrays, slices, maps, and structs — data structures that functions operate on at scale.
Continue the series: Lesson 5 – Control Flow · Lesson 4 – Variables and Types
Browse all tutorials: Content Directory · Go Programming Hub
Frequently Asked Questions
Can Go functions return multiple values?
Yes. List return types in parentheses: func f() (int, error). Callers receive all values: result, err := f().
What does defer do in Go?
defer postpones a function call until the surrounding function returns. Deferred calls run in reverse order (LIFO). It is ideal for cleanup such as closing files or releasing locks.
What is a variadic function?
A variadic function accepts any number of arguments of one type using ... syntax, for example func sum(nums ...int). Inside the function, the parameter is a slice.
Want live Go or backend classes? Join Alkademy for instructor-led programming courses with hands-on projects.
[…] Previous: Lesson 6: Go Functions – Declarations, Returns, and defer […]