Golang中如何处理引用循环错误
Golang中如何处理引用循环错误 大家好
我正在自学一点 text/template 包时,遇到了这个据说很常见的错误。我在 Go 的 Github issues 和这里都稍微搜索了一下,但没有找到答案。我知道存在引用循环,但这个问题不应该已经被处理了吗?这里是一小段代码。
package something
import (
"bytes"
"text/template"
)
type Entry func() string
var Registry []Entry = []Entry{Summary, A, B}
func Summary() string {
buf := &bytes.Buffer{}
str := "{{.}}"
tlp := template.Must(template.New("Summ").Parse(str))
tlp.Execute(buf, Registry)
return buf.String()
}
func A() string {
return "a"
}
func B() string {
return "b"
}
最终会出现类似这样的错误:
# something
something.go:?:?: initialization loop:
.../something.go:?:?: Registry refers to
.../something.go:?:?: Summary refers to
.../something.go:?:?: Registry
更多关于Golang中如何处理引用循环错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我不确定 init 的预期用途是什么,但 rsc 在这里推荐了这种方法。
这很有趣。我可以复现这个问题。我解决这个问题的方法是将 Registry 的初始化放到一个 init 函数中,像这样:
func init() {
Registry = []Entry{Summary, A, B}
}
然后问题似乎就解决了。
感谢您的回答,Sean。我完全忘记了 init()。很高兴知道这是一种解决方法,但查看 https://golang.org/doc/effective_go.html#init,我不确定其原始目的是为了避免那种技术细节。除了这句隐晦的说明 Besides initializations that cannot be expressed as declarations, 之外,init() 的目的似乎是以单例方式初始化内容。
这是一个典型的初始化循环(initialization loop)问题,不是引用循环(reference cycle)。问题在于 Registry 在初始化时直接引用了 Summary 函数,而 Summary 函数在执行时又试图使用 Registry,形成了初始化依赖循环。
问题分析:
Registry的初始化需要调用Summary函数(作为Entry类型值)Summary函数执行时,template.Execute尝试访问Registry变量- 此时
Registry尚未完全初始化,导致循环依赖
解决方案: 将 Registry 的初始化与函数定义分离,使用 init() 函数或在运行时动态构建。
示例代码:
package something
import (
"bytes"
"text/template"
)
type Entry func() string
// 先声明变量,不初始化
var Registry []Entry
func init() {
// 在init函数中初始化,此时所有函数都已定义
Registry = []Entry{Summary, A, B}
}
func Summary() string {
buf := &bytes.Buffer{}
str := "{{.}}"
tlp := template.Must(template.New("Summ").Parse(str))
tlp.Execute(buf, Registry)
return buf.String()
}
func A() string {
return "a"
}
func B() string {
return "b"
}
或者使用函数返回 Registry:
package something
import (
"bytes"
"text/template"
)
type Entry func() string
func GetRegistry() []Entry {
return []Entry{Summary, A, B}
}
func Summary() string {
buf := &bytes.Buffer{}
str := "{{.}}"
tlp := template.Must(template.New("Summ").Parse(str))
tlp.Execute(buf, GetRegistry())
return buf.String()
}
func A() string {
return "a"
}
func B() string {
return "b"
}
关键点: Go 的初始化顺序是确定性的,包级变量的初始化按照声明顺序进行,遇到依赖未初始化变量时就会报初始化循环错误。通过 init() 函数或运行时函数调用可以打破这种循环依赖。

