Just as in many other languages, functions in Go are values. The type of a function is built out of the keyword func and the types of the parameters and return values. This combination is called the signature of the function. Any function that has the exact same number and types of parameters and return values meets the type signature.
Since functions are values, you can declare a function variable:
var myFuncVariable func(string) int
You can assign myFuncVariable any function that has a single parameter of type string and returns a single value of type int. Here’s a longer example:
func f1(a string) int {
return len(a)
}
func f2(a string) int {
total := 0
for _, v := range a {
total += int(v)
}
return total
}
func main() {
var myFuncVariable func(string) int
myFuncVariable = f1
result := myFuncVariable("Hello")
fmt.Println(result)
myFuncVariable = f2
result = myFuncVariable("Hello")
fmt.Println(result)
}
You can run this program on The Go Playground. The output is:
5
500
The default zero value for a function variable is nil. Attempting to run a function variable with a nil value results in a panic (which is covered in the Panic Recover section).
Having functions as values allows you to do some clever things, such as build a simple calculator using functions as values in a map. Let’s see how this works. The following code is available on The Go Playground. First, create a set of functions that all have the same signature:
func add(i int, j int) int { return i + j }
func sub(i int, j int) int { return i - j }
func mul(i int, j int) int { return i * j }
func div(i int, j int) int { return i / j }
Next, create a map to associate a math operator with each function:
var opMap = map[string]func(int, int) int{
"+": add,
"-": sub,
"*": mul,
"/": div,
}
Finally, try out the calculator with a few expressions:
func main() {
expressions := [][]string{
{"2", "+", "3"},
{"2", "-", "3"},
{"2", "*", "3"},
{"2", "/", "3"},
{"2", "%", "3"},
{"two", "+", "three"},
{"5"},
}
for _, expression := range expressions {
if len(expression) != 3 {
fmt.Println("invalid expression:", expression)
continue
}
p1, err := strconv.Atoi(expression[0])
if err != nil {
fmt.Println(err)
continue
}
op := expression[1]
opFunc, ok := opMap[op]
if !ok {
fmt.Println("unsupported operator:", op)
continue
}
p2, err := strconv.Atoi(expression[2])
if err != nil {
fmt.Println(err)
continue
}
result := opFunc(p1, p2)
fmt.Println(result)
}
}
The strconv.Atoi function in the standard library converts a string to an int. The second value returned by this function is an error. As shown earlier, every time you call a function that returns an error, check the returned error value. If it is not nil, handle the error. In this case, we print out the error and skip the rest of the for loop.
You''ll use op as the key to the opMap map and assign the value associated with the key to the variable opFunc. The type of opFunc is func(int, int) int. If there wasn’t a function in the map associated with the provided key, print an error message and skip the rest of the loop. Then call the function assigned to the opFunc variable with the p1 and p2 variables that you decoded earlier. Calling a function in a variable looks just like calling a function directly.
When you run this program, you can see the simple calculator at work:
5
-1
6
0
unsupported operator: %
strconv.Atoi: parsing "two": invalid syntax
invalid expression: [5]
Note
Don’t write fragile programs. The core logic for this example is relatively short. Of the 22 lines inside the for loop, 6 of them implement the actual algorithm, and the other 16 are for error checking and data validation. You might be tempted to not validate incoming data or check errors, but doing so produces unstable, unmaintainable code. Error handling is what separates the professionals from the amateurs.
Function Type Declarations
Just as you can use the type keyword to define a struct, you can use it to define a function type, too. Take a look:
type opFuncType func(int,int) int
You can then rewrite the opMap declaration to look like this:
var opMap = map[string]opFuncType {
// same as before
}
You don’t have to modify the functions at all. Any function that has two input parameters of type int and a single return value of type int automatically meets the type and can be assigned as a value in the map.
What’s the advantage of declaring a function type? One use is documentation. It’s useful to give something a name if you’re going to refer to it multiple times. You will see another use in the related discussion.
Anonymous Functions
Not only can you assign functions to variables, you can also define new functions within a function and assign them to variables. Here’s a program to demonstrate this, which you can run on The Go Playground. The code is also available in the examples/anon_func directory:
func main() {
f := func(j int) {
fmt.Println("printing", j, "from inside of an anonymous function")
}
for i := 0; i < 5; i++ {
f(i)
}
}
Inner functions are anonymous; they don’t have names. To declare an anonymous function, use the keyword func immediately followed by the input parameters, the return values, and the opening brace. It is a compile-time error to put a function name between func and the input parameters.
Just like any other function, you call an anonymous function using parentheses. In this example, you are passing the i variable from the for loop in here. It is assigned to the j input parameter of the anonymous function.
The program gives the following output:
printing 0 from inside of an anonymous function
printing 1 from inside of an anonymous function
printing 2 from inside of an anonymous function
printing 3 from inside of an anonymous function
printing 4 from inside of an anonymous function
You don’t have to assign an anonymous function to a variable. You can write it inline and call it immediately. You can rewrite the previous program to do this:
func main() {
for i := 0; i < 5; i++ {
func(j int) {
fmt.Println("printing", j, "from inside of an anonymous function")
}(i)
}
}
You can run this example on The Go Playground.
Now, this is not something that you would normally do. If you are declaring and executing an anonymous function immediately, you might as well get rid of the anonymous function and just call the code. However, declaring anonymous functions without assigning them to variables is useful in two situations: defer statements and launching goroutines. I’ll talk about defer statements in a bit. Goroutines are covered in the related discussion>.
Since you can declare variables at the package scope, you can also declare package-scope variables and assign them anonymous functions:
var (
add = func(i, j int) int { return i + j }
sub = func(i, j int) int { return i - j }
mul = func(i, j int) int { return i * j }
div = func(i, j int) int { return i / j }
)
func main() {
x := add(2, 3)
fmt.Println(x)
}
Unlike a normal function definition, you can assign a new value to a package-level anonymous function:
func main() {
x := add(2, 3)
fmt.Println(x)
changeAdd()
y := add(2, 3)
fmt.Println(y)
}
func changeAdd() {
add = func(i, j int) int { return i + j + j }
}
Running this code gives the following output:
5
8
You can try this sample on The Go Playground. The code is also in the examples/package_level_anon directory.
Before using a package-level anonymous function, be very sure you need this capability. Package-level state should be immutable to make data flow easier to understand. If a function’s meaning changes while a program is running, it becomes difficult to understand not just how data flows, but how it is processed.