I have the below go code and I am trying to do a recursion. However, the go compiler is complaining of a cyclic reference. Of course I have a cyclic reference; it's a recursion.
I have tried moving the recursion into the main function and out. Same issue. How can I achieve this recursion? The error line is: return lookup(deref.Interface()) // recursion error 'cyclic definition'
christianb@christianb-mac terraform-provider% go build
# github.com/terraform-provider/pkg/foo/repositories.go:773:5: initialization loop:
/Users/christianb/dev/terraform-provider/pkg/foo/repositories.go:773:5: lookup refers to
/Users/christianb/dev/terraform-provider/pkg/foo/repositories.go:774:18: glob..func6.1 refers to
/Users/christianb/dev/terraform-provider/pkg/foo/artifactory/repositories.go:773:5: lookup
type AutoMapper func(field reflect.StructField, thing reflect.Value) map[string]interface{}
var lookup = func() func(payload interface{}) map[string]interface{} {
var handlePtr = func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
deref := reflect.Indirect(thing)
if deref.CanAddr() {
if deref.Kind() == reflect.Struct {
return lookup(deref.Interface()) // recursion error 'cyclic definition'
}
return map[string]interface{}{
field.Tag.Get("hcl"): deref.Interface(),
}
}
return map[string]interface{}{}
}
var checkForHcl = func(mapper AutoMapper) AutoMapper {
return func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
if field.Tag.Get("hcl") != "" {
return mapper(field, thing)
}
return map[string]interface{}{}
}
}
lk := map[reflect.Kind]AutoMapper{}
find := func(payload interface{}) map[string]interface{} {
values := map[string]interface{}{}
var t = reflect.TypeOf(payload)
var v = reflect.ValueOf(payload)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
for i := 0; i < t.NumField(); i {
field := t.Field(i)
thing := v.Field(i)
for key, value := range lk[thing.Kind()](field, thing) {
values[key] = value
}
}
return values
}
lk[reflect.Struct] = checkForHcl(func(f reflect.StructField, t reflect.Value) map[string]interface{} {
return find(t.Interface())
})
lk[reflect.Slice] = checkForHcl(func(field reflect.StructField, thing reflect.Value) map[string]interface{} {
return map[string]interface{}{
field.Tag.Get("hcl"): thing.Interface().([]string),
}
})
lk[reflect.Ptr] = checkForHcl(handlePtr)
return find
}()
CodePudding user response:
Your function cannot reference itself by name, because it is an anonymous function. It only becomes named once it gets assigned to the variable lookup
. Lexically, this only happens once the value being assigned is fully parsed. This is different from a normal func
declaration, where the name is available immediately (which makes recursion rather cleaner):
func myFunc(arg) result {
// ... do something ...
// now we can call the function recursively
return myFunc(arg)
}
In your case, a regular func
declaration won't work, so you need some kind of "forward declaration" to make the name available, which requires a small amount of duplication:
// forward declare the function
var myFunc func(arg) result
myFunc = func(arg) result {
// ... do something ...
// now we can call the function recursively
return myFunc(arg)
}
To do the same thing in a global context, see Burak's answer.
CodePudding user response:
The problem is an initialization loop, not recursion. You are referring to a variable from that variable's definition. You can do:
var lookup func() func(payload interface{}) map[string]interface{}
func init() {
lookup=func() func(payload interface{}) map[string]interface{} {...}
}