Variables of function type

suggest change

Functions are first-class values in Go:

Assigning functions to variables

// intOp is a variable whose type is function that takes
// 2 integers as arguments and returns an integer
var intOp func(int, int) int

func intAdd(a, b int) int {
	return a + b
}

func main() {
	intOp = intAdd
	fmt.Printf("intOp(2, 3) = %d\n", intOp(2, 3))

	// we can assign literal functions as well
	intOp = func(a, b int) int {
		return a * b
	}
	fmt.Printf("intOp(2, 3) = %d\n", intOp(2, 3))
}
intOp(2, 3) = 5
intOp(2, 3) = 6

Passing functions as arguments

func funcAdd(a, b int) int {
	return a + b
}

func runFunc(a, b int, intOp func(int, int) int) {
	fmt.Printf("intOp(%d, %d) = %d\n", a, b, intOp(a, b))
}

func main() {
	runFunc(2, 3, funcAdd)

	// we can pass literal functions as well
	runFunc(2, 3, func(a, b int) int {
		return a * b
	})
}
intOp(2, 3) = 5
intOp(2, 3) = 6

Common uses for function arguments:

Comparing functions

A function variable can only be compared to nil value.

You can't compare a function to another function.

Comparing functions has tricky corner cases so Go designers decided to not implement it at all.

Mocking functionality in tests

Sometimes it's hard to write tests for a piece of code.

Imagine you're writing a web service which needs to authenticate users.

In production deployment this requires looking up user information in the database.

In a test, you don't want to talk to a production database.

One way to enable testing code that calls user authentication is to have two implementations.

One implementation, used in production deployment, talks to the database.

Another implementation is faking the work and is used in tests.

To switch between them we use variable for one level of indirection.

This technique is called mocking.

Here's a sketch of how this might work:

var isUserAdminFn func(string) bool

func isUserAdminProduction(userName string) bool {
    // an real implementation that talks to database
    return false
}

func isUserAdminMock(userName string) bool {
    // a fake implementation used for tests
    return userName == "admin"
}

func isUserAdmin(userName string) bool {
    return isUserAdminFn(userName)
}

func main() {
    isUserAdminFn = isUserAdminProduction
    // in test you would use:
    // isUserAdminFn = isUserAdminMock
}

Feedback about page:

Feedback:
Optional: your email if you want me to get back to you:



Table Of Contents