In html/template, when do you need Template.Clone before adding variant definitions to a shared parsed set?
answer
- one set, one name, one winner
- duplicate the name space, not the text
- each variant needs its own copy
- escaping happens on first execution
- build every copy before serving starts
basics
~20 sClone whenever several variants must define the same name on top of one common base. It duplicates the whole namespace of associated templates so each variant is independent, and in html/template it must happen before the set has executed.
solid answer
~50 sA parsed `*template.Template` is a namespace, so parsing page B's `{{define "content"}}` into the set that already holds page A's replaces A's — every page then renders whichever was parsed last. `Clone` returns a duplicate of the template *together with all its associated templates*, and the copy's namespace is independent, so the idiom is: parse the layout once at startup, then for each variant take `c := template.Must(base.Clone())` and parse that variant's definitions into `c`. Two constraints matter. `html/template` refuses to clone a set that has already been executed, because the first execution runs the contextual escaper over the trees in place — so build every clone during initialisation, never lazily on the first request. And if variants need different helper implementations, clone first and call `Funcs` on the clone; executing a parsed set concurrently is safe, mutating one while it is being executed is not.
code
go · 7 linesvar base = template.Must(template.ParseFS(files, "layouts/base.html"))
// Called at startup, once per page - never from a handler.
func pageSet(file string) *template.Template {
c := template.Must(base.Clone()) // independent name space
return template.Must(c.ParseFS(files, "pages/"+file))
}go deeper
Know that Clone exists, that it copies a parsed template together with everything associated with it, and that it returns an error you must handle or wrap in template.Must.
Explain why two pages defining the same name in one set collide, and how a clone per variant gives each an independent name space to redefine.
Show the startup-time construction: parse the layout once, clone per variant before anything has executed, and treat the resulting sets as immutable while requests are served.
Weigh how many independent template sets a process should hold — memory and startup cost against never mutating a set at request time — and settle that as the house pattern rather than leaving it per package.
## The collision Clone exists to solve Layout inheritance in Go templates works because a page contributes a definition under a name the layout invokes — conventionally `content`. That is fine for one page. With twenty pages, every one of them defines `content`, and a template set maps each name to exactly one body. Parse them all into a single set and the last file parsed wins: every URL renders the same page, with no error anywhere. So the set has to be split. `Clone` is the standard-library answer. ## What Clone gives you `func (t *Template) Clone() (*Template, error)` returns a duplicate of `t` **including every template associated with it**. The documentation states the intent directly: clone is for preparing common templates and then using them with variant definitions added after the clone is made. The underlying parse representations are shared rather than deep-copied text, so the operation is cheap; what is duplicated is the *name space*, which is the part that must be independent. A definition added to the clone does not appear in the original, and vice versa. The idiom, therefore: 1. At startup, parse the layout files once into `base`. 2. For each page, `c := template.Must(base.Clone())`, then parse that page's file into `c`. 3. Keep the resulting per-page sets in a map and execute the layout's name out of the appropriate one. Every page now owns its own `content`, and nothing can overwrite anything. ## The html/template restriction `html/template` is not just `text/template` with an escaping pass bolted on the output side. The first time a set is executed, the package walks its parse trees and rewrites them, inserting escaping appropriate to each syntactic context — HTML text, an attribute value, a URL, a script body. That analysis is done once and recorded in the templates themselves. A clone taken after that would carry trees whose escaping decisions are already fixed, and any definition added to the clone would be inconsistent with them. Rather than produce subtly unsafe output, the package returns an error: an executed template cannot be cloned. Parsing further definitions into an executed set is refused for the same reason. The practical rule is a lifecycle rule. **Everything that changes a template set happens before anything renders.** Parse, `Funcs`, and `Clone` are initialisation-time operations; `Execute` and `ExecuteTemplate` are serving-time operations. A lazily-built cache that clones on the first request for a page is a bug waiting for its second request. ## Concurrency A parsed template may be executed from many goroutines at once; the package is explicit that execution is safe in parallel, with the obvious caveat that two goroutines writing to one `io.Writer` will interleave. What is *not* safe is mutating the set while requests execute it. That is a second, independent reason to do all construction at startup and treat the sets as read-only afterwards. ## Clone and per-variant helpers The same pattern covers variants that need different helper functions rather than different markup: clone the base set and attach the variant's function map to the clone before parsing the text that calls those helpers. The clone keeps one variant's helper table from becoming everybody's. ## Error handling `Clone` returns `(*Template, error)`, and in initialisation code it is almost always wrapped in `template.Must`, which panics on a non-nil error. That is the right severity there: a service that cannot build its template sets should not start. Inside a request path, `Must` would be wrong — but by the rule above you should not be cloning inside a request path at all. ## The short answer Clone when independent variants must define the same name on top of shared material. Do it before the set has executed. Do it at startup. Then never touch the set again.
- What exactly does Clone duplicate?The template and every template associated with it — the whole name space — so definitions added to the copy never appear in the original. The parse representations themselves are shared rather than deep-copied, which is why cloning a large layout set once per page at startup is cheap enough to be the default pattern.
- Why does html/template refuse to clone a set after it has been executed?Because the first execution runs the contextual auto-escaper over the parse trees and rewrites them in place, fixing the escaping for each position. A clone taken afterwards would inherit those fixed decisions, and anything added to it could not be escaped consistently. The package returns an error instead. Clone during initialisation, before anything renders.
- Is it safe to execute one parsed template from many goroutines?Yes, execution is safe in parallel — though two goroutines sharing one writer will interleave their output. What is unsafe is changing the set while requests run: Parse, Funcs and Clone mutate shared state. Do all of them at startup and treat the sets as immutable once serving begins.
saying these in an interview costs you the question
- Parses every page's content definition into one shared set
- Clones lazily on the first request after serving has started
- Thinks Clone deep-copies the template text on every call
- Mutates a live template set while handlers are executing it
- Ignores the error returned by Clone