Functions
By the end of this lesson you'll write Go functions that take typed parameters, return one or many values, accept any number of arguments, capture state as closures, and schedule cleanup with defer — the everyday building blocks of every Go program.
Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ Function Basics: Parameters and Return Type
A function packages code you can reuse by name. The header is func name(parameter type) returnType — you state the type of each parameter and the type it gives back. When neighbouring parameters share a type you write it once ( a, b int ). A function with no return type simply does work and hands nothing back. Read this worked example and run it first.
Your turn. The function header below is missing its parameter type, its return type, and the value it returns. Fill in the three ___ blanks using the hints, then run it.
2️⃣ Multiple and Named Return Values
Go functions can return more than one value , and this is the foundation of how Go reports errors: a function returns its result plus an error , and you check the error before trusting the result. You can also name the return values in the header; then a bare return (a "naked return") hands back those named variables. One rule the compiler enforces: you must use every returned value, or explicitly discard it with the blank identifier _ .
Now you try. minMax already returns two values — your job is to capture them. Fill in the blanks, using _ where a value isn't needed.
3️⃣ Variadic Functions: Any Number of Arguments
Sometimes you don't know how many arguments you'll get — think "sum all of these". A variadic parameter, written nums ...int , accepts zero or more values, and inside the function nums is just a slice ( []int ) you can range over. If you already have a slice, "spread" it into the call with slice... .
4️⃣ Functions as Values and Closures
In Go a function is a first-class value : you can store it in a variable, pass it to another function, and return it from one. A function returned this way can "remember" variables from where it was created — that's a closure . Each closure gets its own private copy of those variables, which survives between calls. This is how you build counters, generators, and configurable behaviour without globals.
5️⃣ defer: Cleanup That Always Runs
defer schedules a function call to run when the surrounding function exits — perfect for cleanup like file.Close() right next to where you opened it, so you can never forget it. Two rules to burn in: deferred calls run in LIFO order (last deferred, first to run), and a defer's arguments are evaluated immediately , even though the call happens later. Watch both in the output below.
Common Errors (and the fix)
- "declared and not used": you captured a return value but never read it. Either use it, or discard it with the blank identifier — instead of when you don't need result .
- "not enough return values" / "too many return values": your return doesn't match the header. If the signature says , every return must supply both , e.g. on success and on failure.
- "cannot use x (type string) as type int": an argument's type doesn't match the parameter type in the header. Go won't auto-convert — pass the right type, e.g. add(3, 7) , not .
- defer printed the "wrong" (old) value: that's by design — captures x 's value at the defer line , not at exit. Wrap it in a closure — — if you want the value at exit.
- "assignment mismatch: 2 variables but minMax returns ... ": the number of variables on the left of := must equal the number of values returned. Add a _ for each value you skip.
Pro Tips
- 💡 Return the error last: the Go convention is , with error as the final value. Stick to it and your code reads like everyone else's.
- 💡 defer right after you acquire: open a file, then immediately defer f.Close() . The cleanup lives next to the setup, so it can't be forgotten on an early return.
- 💡 Keep naked returns short: named returns plus a bare return are great for 3-line functions but get hard to follow in long ones — list the values explicitly there.
- 💡 Variadic must come last: a ...T parameter has to be the final one in the list, e.g. .
📋 Quick Reference
Pattern
Go Syntax
Result / Note
Function
typed params + return type
No return
does work, returns nothing
Multi-return
result + error pattern
Named return
bare return hands back n
Discard value
ignore with blank identifier
Variadic
zero or more args (a slice)
Spread slice
pass a slice as args
Function value
store a func in a variable
Defer
runs at exit, LIFO order
Frequently Asked Questions
Mini-Challenge: Stats Helper
No blanks this time — just a brief and an outline. Write the whole stats function yourself, combining a variadic parameter with multiple return values. Run it and check your output against the expected line.
🎉 Lesson Complete!
- ✅ A function is func name(param type) returnType — typed params, typed return
- ✅ Go returns multiple values ; the pattern is everywhere
- ✅ Named returns let a bare return hand back the named variables
- ✅ Use every return value, or discard it with the blank identifier _
- ✅ Variadic ...int takes any number of args; spread a slice with slice...
- ✅ Functions are values ; closures keep their own private state
- ✅ defer runs cleanup at exit in LIFO order, capturing arguments immediately
- ✅ Next lesson: Structs and Interfaces — Go's approach to data and composition
Practice quiz
What is the shape of a Go function header?
- func returnType name(params)
- def name(params) -> type
- func name(parameter type) returnType
- function name(params): type
Answer: func name(parameter type) returnType. Go writes func name(parameter type) returnType, with parameter types after the names.
For func add(a, b int) int, what does the shared type mean?
- both a and b are int
- a is int, b is untyped
- a and b are different types
- it is a syntax error
Answer: both a and b are int. When neighbouring parameters share a type you list it once; a and b are both int.
By Go convention, the error is which return value?
- the first
- the only
- it is never returned
- the last
Answer: the last. Fallible functions return (result, error) with error as the final value.
What does a bare return require?
- no special setup
- named return values in the header
- a single return value
- a deferred call
Answer: named return values in the header. A naked (bare) return hands back the named return variables declared in the header.
What does fmt.Println(sum()) print for a variadic func sum(nums ...int) int?
- 0
- nil
- an error
- nothing compiles
Answer: 0. Calling a variadic with no arguments is allowed; the empty sum is 0.
How do you spread a slice scores into a variadic call?
- sum(scores)
- sum(...scores)
- sum(scores...)
- sum(&scores)
Answer: sum(scores...). Append ... after the slice: sum(scores...) passes its elements as the variadic args.
What must you do with every value a function returns?
- log it
- use it or discard it with _
- store it in a global
- ignore it freely
Answer: use it or discard it with _. Go won't compile if you capture a value you never read; discard with the blank identifier _.
When are a deferred call's arguments evaluated?
- when the function exits
- never
- at the next defer
- immediately at the defer statement
Answer: immediately at the defer statement. Arguments are evaluated at the defer line; the call itself runs at function exit.
In what order do multiple deferred calls run?
- FIFO (first deferred, first run)
- LIFO (last deferred, first run)
- alphabetical
- random
Answer: LIFO (last deferred, first run). Defers run in LIFO order — the last deferred call runs first.
A function stored in a variable that remembers surrounding variables is called...
- a method
- a goroutine
- a closure
- an interface
Answer: a closure. A closure captures variables from where it was created and keeps its own state.
Continue this course
- Previous: Variables and Data Types
- Next: Structs and Interfaces