Golang模板嵌套循环中自定义计数器变量更新打印问题排查
问题原因
- 上下文被覆盖:你使用
{{with $i := 0}}声明变量时,with块会把当前作用域的.(上下文)直接替换为0这个整数值。后续你写的.Numbers、.Letters本质是尝试从整数0上获取对应字段,根本读不到你传入的data结构体,模板执行直接报错。 - 版本兼容问题:Go 1.11版本之前的
text/template库不支持对已声明变量做重新赋值(也就是{{$i = add $i 1}}这种写法不生效),即使是支持该语法的高版本,也需要避免上下文被覆盖的问题。
修复方案
方案1:高版本Go直接修复上下文
如果你使用Go 1.11及以上版本,只需要保留顶层根数据的引用,不使用with覆盖上下文即可正常运行:
var tmplSrc = `start {{$root := .}} {{$i := 0}} {{range $number := $root.Numbers}} {{range $letter := $root.Letters}} {{if eq $letter "b"}} {{$i = add $i 1}} {{$i}} {{end}} {{end}} {{end}} fin `
运行后会依次输出1 2 3 4,符合计数预期。
方案2:全版本兼容的引用类型计数器
如果需要兼容低版本Go,可以用引用类型(比如map)作为计数器,修改引用指向的内容不受模板作用域限制:
- 先在FuncMap中添加
dict工具函数用于创建map(标准库模板默认没有内置该函数):
funcMap := template.FuncMap{ "add": func(a int, b int) int { return a + b }, "dict": func(values ...interface{}) (map[string]interface{}, error) { if len(values)%2 != 0 { return nil, errors.New("invalid dict call") } dict := make(map[string]interface{}, len(values)/2) for i := 0; i < len(values); i += 2 { key, ok := values[i].(string) if !ok { return nil, errors.New("dict keys must be strings") } dict[key] = values[i+1] } return dict, nil }, }
- 调整模板部分:
var tmplSrc = `start {{$root := .}} {{$counter := dict "val" 0}} {{range $number := $root.Numbers}} {{range $letter := $root.Letters}} {{if eq $letter "b"}} {{$counter.val = add $counter.val 1}} {{$counter.val}} {{end}} {{end}} {{end}} fin `
修改$counter.val本质是修改map内部的值,所有作用域都能读到更新后的数据。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

