Reputation: 6692
I am moving to Go from Node.js and I am concerned whether a construct that I would use in Node is safe to do in Go and whether there is a more idiomatic way to accomplish the same thing. I am using the Echo framework and want to set a route specific struct that will be available within the context object. I could generate the struct for every call within middleware, but it's expensive to do so. Instead, I set the struct once in an outer func that then returns an inner func that refers to the struct in the outer func. My hope is that I then only incur the generation cost once and then have the correct struct associated with my route for every call.
e.POST(path, POST.GenericPostHandler, func(next echo.HandlerFunc) echo.HandlerFunc {
operation := getOperationMap(path)
return func(c echo.Context) error {
c.Set("op", operation)
return next(c)
}
})
Are there any concerns with this code? Will it cause problems with GC? Is there a more efficient way to accomplish the same thing? I assume a copy of the struct is made every time the middleware is called.
Upvotes: 0
Views: 71
Reputation: 351536
This code is safe, won't cause GC problems, and is a good, idiomatic pattern that can be used in Go.
In your example, only one operation
will be created, moved to the heap, and then shared by each request as they are handled by Echo.
I often use this exact pattern myself when I need to initialize an expensive struct that will be used when handling all requests.
Upvotes: 1
Reputation: 4139
If operationMap
never changes after initialization, You can declare operationMap
as a singleton instance like following:
package main
import (
"fmt"
"sync"
)
var (
operationMapInst map[string]string // I don't know the exact type of map, so you should change the type.
operationMapOnce sync.Once
)
func getOperationMap() map[string]string {
// operationMapOnce.Do() runs only once
// when the first time getOperationMap() is called.
operationMapOnce.Do(func() {
// Initialize operationMapInst.
operationMapInst = map[string]string{"/": "root", "/ver": "version"}
fmt.Println("operaionMap has initialized!")
})
return operationMapInst
}
func main() {
// The initialization logic runs only once.
// Because getOperationMap() returns map,
// syntax for the value for a path should be getOperationMap()[path],
// not getOperationMap(path).
rootOp, ok := getOperationMap()["/"]
fmt.Println(rootOp, ok)
// repetition
rootOp, ok = getOperationMap()["/"]
fmt.Println(rootOp, ok)
verOp, ok := getOperationMap()["/ver"]
fmt.Println(verOp, ok)
verOp, ok = getOperationMap()["/ver"]
fmt.Println(verOp, ok)
}
You can run this code here.
I recommend http://marcio.io/2015/07/singleton-pattern-in-go/ for understanding singleton pattern in Go.
Upvotes: 0