Keywords like go
and defer
expect a function call as parameters. Is there a type available that can be used the same way? (e.g. to write a function that expects a function call - opposed to a function - as argument).
No there is not. You can't do the same with your function.
go
and defer
are backed by the language spec and the rule is enforced by the compiler.
What you may do is use a variable / value of function type, which you may call later / at any time as if it would be a function.
For example:
func myFunc() {
fmt.Println("hi")
}
func main() {
var f func()
f = myFunc
f() // This calls the function value stored in f: myFunc in this example
}
Edit: To have the functionality you mentioned in the comment: just wrap the function call with its arguments in a func()
, and use / pass that.
For example:
func launch(f func()) {
fmt.Println("Before launch")
go func() {
defer fmt.Println("After completion")
f()
}()
}
Using it:
func main() {
launch(func() {
fmt.Println("Hello, playground")
})
time.Sleep(time.Second)
}
Which outputs (try it on the Go Playground):
Before launch
Hello, playground
After completion
Yes, this is not an exact workaround. If the params may change, you have to make a copy of them before calling launch()
, and use the copy in the function literal (closure), like in this example:
s := "Hello, playground"
s2 := s // make a copy
launch(func() {
fmt.Println(s2) // Use the copy
})
s = "changed"
For a concrete function type we may construct a helper function which provides us automatic parameter saving. This helper function must have identical signature, and return a function without parameters. The returned function is a closure which calls the original function with the parameters. The act of calling this helper function is the mechanism to save the parameters, so the usage is identical to that of defer
.
For example the helper for fmt.Println(s)
is:
func wrapPrintln(s string) func() {
return func() {
fmt.Println(s)
}
}
And using it:
launch(wrapPrintln(s))
Example for a function with 2 int
parameters:
func Sum(a, b int) {
fmt.Println("Sum:", a+b)
}
func WrapSum(a, b int) func() {
return func() {
Sum(a, b)
}
}
launch(WrapSum(a, b))
The above WrapPrintln()
and WrapSum()
wrapped a concrete function, and it can't be used for other functions (the wrapped function is "wired in"). We can make the wrapped functions a parameter too:
func WrapFuncIntInt(f func(a, b int), a, b int) func() {
return func() {
f(a, b)
}
}
And we may use it like this:
launch(WrapFuncIntInt(Sum, a, b))
Try this one on the Go Playground.
You may use reflection to avoid having to make manual copies, but in this solution we're not actually calling the function, just passing it. Also due to using reflection, it will be slower. Another advantage is that this "feels" generic (we may use functions with different signatures), but we lose compile-time safety.
func launch(f interface{}, params ...interface{}) {
fmt.Println("Before launch")
go func() {
defer fmt.Println("After completion")
pv := make([]reflect.Value, len(params))
for i, v := range params {
pv[i] = reflect.ValueOf(v)
}
reflect.ValueOf(f).Call(pv)
}()
}
Example calling it:
func main() {
i, s := 1, "Hello, playground"
launch(fmt.Printf, "%d %q\n", i, s)
i, s = 2, "changed"
time.Sleep(time.Second)
}
Which outputs (try it on the Go Playground):
Before launch
1 "Hello, playground"
After completion
There is a single exception which we may use. This is the Method value. If x
has static type T
and T
's method set contains the method M
, we may use x.M
(without calling it).
The expression x.M
is a method value, and it saves a copy of x
which will be used as the receiver when the expression's result (which is a function value) is called.
Example:
type myParams struct {
format string
i int
s string
}
func (mp myParams) Call() {
fmt.Printf(mp.format, mp.i, mp.s)
}
func main() {
p := myParams{format: "%d %q\n", i: 1, s: "Hello, playground"}
launch(p.Call) // p is saved here
p.i, p.s = 2, "changed"
time.Sleep(time.Second)
}
func launch(f func()) {
fmt.Println("Before launch")
go func() {
defer fmt.Println("After completion")
f()
}()
}
It outputs the same. Try it on the Go Playground.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With