In the general case
This is a question of design. Interfaces offer something that functions don’t : dynamic dispatch. So if later on you want (possibly your own) client code to apply said function to an object, and envision that this object may possibly be of one of several different types at one given point in the program, go for an interface.
The pro : you can get flexible.
The cons :
- dynamic dispatch costs a minor overhead in execution time. You won’t want that in the very middle of a critical loop for instance.
- interfaces, if available, will be used as the system grows, possibly in slightly unexpected ways. Which means that whereas most times you can define a function’s scope of responsiblity light-heartedly enough, an interface’s responsibility and type signature should be well thought-out, as a design decision.
- there are more intellectual indirections to go through for a reader who tries to understand the code.
Go is designed as a simple and pragmatic language. I suppose that as willing users, we should carry forward its philosophy. Hence, I’d say : if you don’t need something, don’t use it. 🙂
In your particular case
Although a function pointer is indeed a form of developer-managed dynamic dispatch, it seems to me that the reasoning above remains applicable : go for the simplest solution that could satisfy the foreseeable needs. Otherwise Go will become Java. If you’re certain that the delegate will never need to provide any other entry point (like providing meta information for example), I’d say stick to the function pointer.