Question
In Java, I can write something like this to pass behavior into another method:
someMethod(new Runnable() {
public void run() {
// run this sometime later
}
});
This allows the method to store or execute that code later, although using an anonymous inner class can be awkward.
Does Go provide a way to pass a function or callback as a parameter so that it can be executed later?
Short Answer
By the end of this page, you will understand that functions are first-class values in Go, which means you can store them in variables, pass them into other functions, and call them later. You will also see how Go callbacks compare to Java's Runnable style, how to declare function parameters, and how closures make callback-style code practical and concise.
Concept
In Go, functions can be passed as parameters just like numbers, strings, or structs. This works because functions are first-class values.
That means you can:
- assign a function to a variable
- pass a function into another function
- return a function from a function
- call a function later
This is Go's simple and direct way to support callbacks.
In Java, older callback patterns often use interfaces such as Runnable, especially before lambdas became common. In Go, you usually do not need a special interface just to pass executable behavior. You can pass the function itself.
For example, if a function expects another function as a parameter, you can write its parameter type like this:
func doLater(task func()) {
task()
}
Here, task is a parameter whose type is func(), meaning:
func= function type()= it takes no arguments- no return type = it returns nothing
This matters in real programming because many common tasks use callbacks or delayed behavior, including:
- event handling
- sorting with custom logic
- processing collections
- retry logic
- goroutines and concurrency helpers
Mental Model
Think of a function in Go like a recipe card.
- A normal value like
5is a finished ingredient. - A function value is a set of instructions someone can use later.
When you pass a function as a parameter, you are handing another part of the program a recipe card and saying:
"Use these instructions when you're ready."
If the function also remembers values from where it was created, that is called a closure. It is like a recipe card with handwritten notes already attached.
Syntax and Examples
In Go, you declare a function parameter by writing its function type.
Basic syntax
func execute(task func()) {
task()
}
This means execute accepts a function that:
- takes no parameters
- returns no value
Passing a named function
package main
import "fmt"
func sayHello() {
fmt.Println("Hello")
}
func execute(task func()) {
task()
}
func main() {
execute(sayHello)
}
What happens?
sayHellois a normal function.executeaccepts a function parameter.execute(sayHello)passes the function itself, not the result.- Inside
execute, runs it.
Step by Step Execution
Consider this example:
package main
import "fmt"
func runTask(task func()) {
fmt.Println("Before task")
task()
fmt.Println("After task")
}
func main() {
message := "Callback executed"
runTask(func() {
fmt.Println(message)
})
}
Step-by-step
-
The program starts in
main(). -
messageis created with the value"Callback executed". -
runTask(...)is called. -
The argument passed to
runTaskis an anonymous function:func() { fmt.Println(message) } -
Inside , the line below runs first:
Real World Use Cases
Passing functions as parameters is common in Go.
1. Custom sorting or processing
You may want to define custom logic for comparing or transforming values.
numbers := []int{5, 2, 9}
A helper function could accept a callback to decide how values are handled.
2. Retry logic
A reusable retry function can accept the operation to attempt.
func retry(task func() error) error {
return task()
}
This lets you reuse the retry mechanism for API calls, file reads, or database operations.
3. HTTP middleware and handlers
Web applications often pass functions around to wrap behavior such as:
- authentication
- logging
- rate limiting
- error recovery
4. Deferred cleanup helpers
You might pass cleanup actions into utility functions.
5. Asynchronous or concurrent work
Functions are often passed into goroutines:
go {
fmt.Println()
}()
Real Codebase Usage
In real Go codebases, function parameters are often used in practical, maintainable patterns.
Guarded execution
A helper may validate inputs before running a callback.
func withValidUser(name string, action func()) {
if name == "" {
return
}
action()
}
This uses an early return to avoid unnecessary work.
Error-producing callbacks
A very common pattern is passing functions that return error.
func run(task func() error) error {
return task()
}
This fits naturally with Go's error handling style.
Dependency injection for testability
Instead of hardcoding behavior, a function can receive another function that performs work.
func fetchData(load func() string) {
load()
}
Common Mistakes
1. Calling the function instead of passing it
Broken code:
execute(sayHello())
Why it is wrong:
sayHello()calls the function immediately- its result is passed instead of the function itself
- if
sayHelloreturns nothing, the code will not compile
Correct code:
execute(sayHello)
2. Using the wrong function signature
Broken code:
func execute(task func()) {}
func greet(name string) {}
execute(greet)
Why it is wrong:
executeexpectsfunc()greetisfunc(string)- the signatures must match exactly
3. Forgetting that closures capture outer variables
Example:
Comparisons
Go function callbacks vs Java Runnable
| Idea | Go | Java classic style |
|---|---|---|
| Pass behavior | Pass a function directly | Often pass an object implementing an interface |
| Syntax | Usually short | Often more verbose |
| Inline logic | Anonymous function | Anonymous inner class or lambda |
| State capture | Closures | Anonymous class or lambda capture |
Named function vs anonymous function
| Option | When to use | Example |
|---|---|---|
| Named function | Reusable logic | execute(sayHello) |
Cheat Sheet
Core syntax
func use(task func()) {
task()
}
Function type examples
func() // no params, no return
func(int) // one int param
func(string) error // one string param, returns error
func(int, int) int // two ints in, one int out
Pass a named function
func hello() {
fmt.Println("hello")
}
use(hello)
Pass an anonymous function
use(func() {
fmt.Println()
})
FAQ
Can you pass a function as an argument in Go?
Yes. Functions are first-class values in Go, so they can be passed to other functions as parameters.
How do callbacks work in Go?
A callback is usually just a function passed into another function, which then calls it at the appropriate time.
Does Go need interfaces like Java Runnable for simple callbacks?
No. If you only need one action, passing a function directly is usually simpler than defining an interface.
Can anonymous functions be used in Go?
Yes. Go supports anonymous functions, and they are commonly used for inline callbacks.
Can a Go function remember values from its surrounding scope?
Yes. This is called a closure. The function can access variables from the place where it was created.
What happens if I call a nil function in Go?
Your program will panic at runtime. Check whether the function is nil before calling it if that is possible in your code.
Can a function parameter return a value in Go?
Yes. For example, you can accept a parameter of type func(int, int) int and use its returned result.
Is passing functions common in real Go programs?
Yes. It is widely used in middleware, retries, helper utilities, testing, and custom processing logic.
Mini Project
Description
Build a small task runner that accepts functions as parameters and executes them in order. This demonstrates how Go uses function values for callbacks without needing interface-heavy patterns.
Goal
Create a program that stores several tasks as functions and runs them one by one.
Requirements
- Define a function that accepts a callback of type
func(). - Create at least one named function task.
- Pass at least one anonymous function as a task.
- Print messages before and after each task runs.
- Run multiple tasks from
main().
Keep learning
Related questions
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Calling Functions Across Files in the Same Go Package
Learn how Go uses packages across multiple files, why functions may appear undefined, and how to organize code correctly.