go template 源码的一个疑问

49 天前
 yujianwjj
// Parse parses text as a template body for t.
// Named template definitions ({{define ...}} or {{block ...}} statements) in text
// define additional templates associated with t and are removed from the
// definition of t itself.
//
// Templates can be redefined in successive calls to Parse.
// A template definition with a body containing only white space and comments
// is considered empty and will not replace an existing template's body.
// This allows using Parse to add new named template definitions without
// overwriting the main template body.
func (t *Template) Parse(text string) (*Template, error) {
	t.init()
	t.muFuncs.RLock()
	trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins())
	t.muFuncs.RUnlock()
	if err != nil {
		return nil, err
	}
	// Add the newly parsed trees, including the one for t, into our common structure.
	for name, tree := range trees {
		if _, err := t.AddParseTree(name, tree); err != nil {
			return nil, err
		}
	}
	return t, nil
}

我认为应该是下面这样,返回值没必要加原来的 *Template

func (t *Template) Parse(text string) error 

注释里面写了 Templates can be redefined in successive calls to Parse.

但是没明白什么场景会 redefined 。

1630 次点击
所在节点    Go 编程语言
19 条回复
matrix1010
49 天前
文档里的第一个例子就很清楚: https://pkg.go.dev/text/template. 不返回就没法继续执行了。Templates can be redefined in successive calls to Parse 是说可以多次 call `Parse`来重用,跟返回什么关系不大
Kinnice
49 天前
比如加密模版?
flyqie
49 天前
能复用还是尽量复用的比较好,template 这种复用需求还是有不少的。
uniquecolesmith
49 天前
确实如此,你思考的很深,去掉也可以的,应该只是设计者一开始的设计一直延续,用多了就不好改

每日分享 Go / 云原生 / Python / 前端等开源技术,有兴趣的可以加我,附上你的职位,我拉你进相关微信分享群,这里只做开源技术分享,加我 (wechat: _whatwewant_)
uniquecolesmith
49 天前
@yujianwjj 这个问题在群里问,有更多大佬为你解答
bv
49 天前
前 3L 不知道想表达什么,我个人觉得这么定义 func (t *Template) Parse(text string) error 确实可行,但不清除设计这个接口时为何要返回 *Template
GenericT
49 天前
go 不就是这个风格,a := append(a,b)
baiyi
49 天前
这个写法是为了方便链式调用的,你看下 template 的其他方法,也都是返回了同样值。
返回了 err 的话不能直接进行调用,但可以通过 template.Must 这种方式写成一行。
使用的时候可以这么写:template.Must(template.New("prog").Parse(prog)).Execute(&buf, v)
yujianwjj
49 天前
append 这个风格是因为可能会触发底层数组扩容,重新分配数组,所以设计成这个样子。
maocat
49 天前
8 楼说的才是对的,你看源码顶上有个 Must 方法,给你链式调用用的
bv
49 天前
@maocat 你确定是因为为了链式调用,Parse 返回了 (*Template, error) 两个参数,还怎么做链式调用?
evercyan
49 天前
@bv #11

// Must is a helper that wraps a call to a function returning (*Template, error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as
//
// var t = template.Must(template.New("name").Parse("html"))
func Must(t *Template, err error) *Template {
if err != nil {
panic(err)
}
return t
}
maocat
49 天前
@bv 你能不能去看看源码啊,示例都摆在源码的头上了
bv
48 天前
@evercyan 看到了,通过 Must 调用确实可以做到链式调用。但是这么设计总感觉是本末倒置呀。
bv
48 天前
我的疑惑就是:如果 Parse 不返回 *Template ,也不影响 Must 的链式调用。

func Must(t *Template, s string) *Template {
if err := t.Parse(s); err != nil {
panic(err)
}
return t
}

type Template struct{}

func (t *Template) Parse(s string) error {
return nil
}
evercyan
48 天前
@bv #15

链式的实现是每次调用都返回自身, 也就是 *Template, 实现 t.A().B().C(),
不然如果 A() 执行返回了 error, 还能 err.B().C() 么,
但是如果 A() 执行确实有需要抛出去的 error 怎么办, 那就只能搞个 Must 了,
当然并不是说这个返回就一定是为了链式调用而设计的,
bv
48 天前
@evercyan 感谢,这个规则我明白,Parse(text string) (*Template, error) 反响适配 Must 感觉很奇怪,我感觉 #15 的 Must 写法也不影响 Must 链式调用。
evercyan
48 天前
@bv #17 这种在 Must 里去做 Parse 没看懂是什么逻辑, 如果还有一个 Parse1 呢,
这里的 Must 其实都可以抽成公共函数用泛型去实现了, 本身不带业务逻辑的,
就单纯判断最后一个入参是 error 就 panic, 不是就返回第一个 Any,
matrix1010
48 天前
再详细说说: 1. 为什么返回 (*Template, error), 因为这样 API 更简洁。比如 templates["index"] = template.Must(template.Parse("xxxxx")),直接一行写完. 2. 什么情况下会重复 call Parse: Parse 方法调用时的实际情况取决于你传入的 string, 当 template 本身是动态的情况下可能会在运行时 parse ,很多可以客户定制的低代码平台应该都有这类功能。可以看我写的这个例子: https://go.dev/play/p/rE7IDfsPBLp

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1029062

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX