V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
yujianwjj
V2EX  ›  Go 编程语言

go template 源码的一个疑问

  •  
  •   yujianwjj · 28 天前 · 1545 次点击
    // 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 。

    19 条回复    2024-04-02 19:06:38 +08:00
    matrix1010
        1
    matrix1010  
       28 天前
    文档里的第一个例子就很清楚: https://pkg.go.dev/text/template. 不返回就没法继续执行了。Templates can be redefined in successive calls to Parse 是说可以多次 call `Parse`来重用,跟返回什么关系不大
    Kinnice
        2
    Kinnice  
       28 天前
    比如加密模版?
    flyqie
        3
    flyqie  
       28 天前 via Android
    能复用还是尽量复用的比较好,template 这种复用需求还是有不少的。
    uniquecolesmith
        4
    uniquecolesmith  
       28 天前
    确实如此,你思考的很深,去掉也可以的,应该只是设计者一开始的设计一直延续,用多了就不好改

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

    链式的实现是每次调用都返回自身, 也就是 *Template, 实现 t.A().B().C(),
    不然如果 A() 执行返回了 error, 还能 err.B().C() 么,
    但是如果 A() 执行确实有需要抛出去的 error 怎么办, 那就只能搞个 Must 了,
    当然并不是说这个返回就一定是为了链式调用而设计的,
    bv
        17
    bv  
       28 天前
    @evercyan 感谢,这个规则我明白,Parse(text string) (*Template, error) 反响适配 Must 感觉很奇怪,我感觉 #15 的 Must 写法也不影响 Must 链式调用。
    evercyan
        18
    evercyan  
       28 天前
    @bv #17 这种在 Must 里去做 Parse 没看懂是什么逻辑, 如果还有一个 Parse1 呢,
    这里的 Must 其实都可以抽成公共函数用泛型去实现了, 本身不带业务逻辑的,
    就单纯判断最后一个入参是 error 就 panic, 不是就返回第一个 Any,
    matrix1010
        19
    matrix1010  
       28 天前
    再详细说说: 1. 为什么返回 (*Template, error), 因为这样 API 更简洁。比如 templates["index"] = template.Must(template.Parse("xxxxx")),直接一行写完. 2. 什么情况下会重复 call Parse: Parse 方法调用时的实际情况取决于你传入的 string, 当 template 本身是动态的情况下可能会在运行时 parse ,很多可以客户定制的低代码平台应该都有这类功能。可以看我写的这个例子: https://go.dev/play/p/rE7IDfsPBLp
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   799 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 20:47 · PVG 04:47 · LAX 13:47 · JFK 16:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.