July 7, 2026

Go Functions – Declarations, Returns, and defer

Updated July 2, 2026. Refreshed for Go 1.22+ and current SEO best practices.

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.


0 0 votes
Article Rating
Subscribe
Notify of
1 Comment
Oldest
Newest Most Voted
trackback

[…] Previous: Lesson 6: Go Functions – Declarations, Returns, and defer […]

1
0
Would love your thoughts, please comment.x
()
x