What does regexp.MustCompile do differently from regexp.Compile, and where should it be called?
answer
- same work, no error return
- the prefix is a naming convention
- ask where the argument came from
- literal in source versus data from outside
- fails at process start, not mid-request
basics
~20 sregexp.Compile returns (*Regexp, error); regexp.MustCompile returns only the *Regexp and panics if the pattern is malformed. Use it where the pattern is a literal in your source, typically a package-level var or init, never on caller or config input.
solid answer
~50 s`regexp.Compile` returns `(*regexp.Regexp, error)`; `regexp.MustCompile` takes the same argument, returns just the `*regexp.Regexp`, and panics if the pattern will not parse. The `Must` prefix is a naming convention across the standard library — `template.Must` is the general form, taking the value-and-error pair a `Parse` call returns. Panicking is acceptable there because the argument is a literal the programmer wrote: a bad pattern is a build bug, no caller could handle it, and package-level variables are initialised before `main`, so the failure lands at process start and in CI. The moment the pattern comes from a config file, a request or an environment variable it is data, and data is allowed to be wrong, so you switch to `Compile` and return a wrapped error. The `Must` prefix is a promise about the call site, not about the value.
code
go · 11 lines// Fine: the pattern is a literal in this file, so a typo fails at process start.
var ruleNamePattern = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
// Not fine as MustCompile: the pattern comes from a config file an operator edits.
func compileOperatorRule(pattern string) (*regexp.Regexp, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, fmt.Errorf("bad rule pattern %q: %w", pattern, err)
}
return re, nil
}go deeper
Remember the pair: Compile returns a value and an error, MustCompile returns the value alone and panics on a bad pattern. Recall that it belongs next to a package-level var declaration.
Explain why the panic is acceptable there — the argument is a source literal, so the failure is a build bug that surfaces before main runs — and name template.Must as the general value-and-error form.
Show the failure mode you have seen: a Must call whose argument became dynamic, turning an operator typo into a crash while serving. Describe how you catch it and what you replace it with.
Turn it into a rule others can apply without you: where Must calls are permitted, who reviews an exception, and why the grep-able convention beats case-by-case judgment.
### The convention `Must` is a naming convention in Go, not a language feature. A function whose name begins with `Must` does the same job as its plain sibling, but instead of returning an error it **panics** if the operation fails. It exists so that a value which is guaranteed correct at compile time can be assigned to a package-level variable, where there is no place to put an `if err != nil`. The two shapes you will meet: ```go func MustCompile(str string) *Regexp // regexp func Must(t *Template, err error) *Template // text/template and html/template ``` `regexp.MustCompile` takes the same argument as `regexp.Compile` and returns only the `*regexp.Regexp`, panicking if the pattern does not parse. `template.Must` is the more general form: it takes the `(value, error)` pair that a call such as `template.New("page").Parse(src)` returns, panics if the error is non-nil, and otherwise hands back the value. Both give you an expression that can appear on the right-hand side of a `var` declaration. ### Why panicking is acceptable here Look at what the argument actually is: ```go var ruleNamePattern = regexp.MustCompile(`^[a-z][a-z0-9-]*$`) ``` The pattern is a **literal written by the programmer**. If it is malformed, the code is wrong, and it is wrong for every run of the program, every user and every input. There is no error handling a caller could usefully perform — you cannot "retry" a typo. And because package-level variables are initialised before `main` runs, the panic happens at process start, in your own tests and in CI, long before a request arrives. That is the whole bargain: **a Must helper converts a programmer mistake into an immediate, loud, unmissable startup failure.** The same reasoning covers a `func init()` that parses embedded templates, or a `var` holding a parsed time layout. The value is fixed at build time; failing to build it is a build bug. ### Where it stops being acceptable The moment the argument stops being a literal, the reasoning collapses. A pattern read from a configuration file, a query parameter, a database column or an environment variable is **data**, and data is allowed to be wrong. Passing it to `regexp.MustCompile` means an operator's typo in a config file crashes the process — and if the call sits inside an HTTP handler or a worker, it crashes the process *while serving*, with a stack trace instead of a message telling the operator which rule is broken. So the discipline is a call-site rule, not a function rule: - Package-level `var`, `func init`, and tests: `Must` is fine and idiomatic. - Anywhere the argument came from outside the source file: use the plain `Compile` / `Parse` form, wrap the error with context, and return it. Tests are worth calling out. Inside a `_test.go` file the whole point is to fail loudly, and helpers that panic (or call `t.Fatal`) are fine — the "process" that dies is the test binary. ### Writing your own With generics the general helper is three lines, and many codebases have one: ```go func Must[T any](v T, err error) T { if err != nil { panic(err) } return v } ``` If you export such a helper, document it the way the standard library does — that it is intended for variable initialisation with values known to be valid — because the name alone is what tells a reader "this panics". A reviewer's shortcut is exactly that: **the `Must` prefix is a promise about the call site, not about the value.** Seeing `Must` anywhere near caller-supplied data is the signal to push back. ### The trap in review The characteristic defect is a refactor that *moves* a working `MustCompile` from a package-level `var` into a function so it can take a parameter. The diff looks harmless — the same call, one level in — but the argument's provenance changed, and the crash moved from startup into the request path. When you see that in a pull request, the fix is to compile once at start-up if the pattern is static, or to switch to `regexp.Compile` and return an error if it is not.
- What arguments does template.Must take, and why is its shape different?`template.Must(t *Template, err error) *Template` takes the pair that a call such as `template.New("page").Parse(src)` returns, panics if the error is non-nil, and otherwise returns the template. That shape wraps any function returning a value and an error, so it works in a var declaration where no `if err != nil` can go.
- How would you write a Must helper for your own package?With generics it is three lines: `func Must[T any](v T, err error) T { if err != nil { panic(err) }; return v }`. If you export it, document that it is meant for variable initialisation with values known valid at build time — the name is what warns a reader it panics.
- Is a Must helper acceptable inside test files?Yes, freely. The process that dies is the test binary, and failing loudly is the point of a test. Setup helpers in `_test.go` files commonly panic or call `t.Fatal` rather than threading errors through fixture code.
saying these in an interview costs you the question
- Thinks Must is a language keyword rather than a naming convention
- Calls MustCompile on a pattern read from configuration
- Recompiles the same literal pattern inside a hot function
- Believes MustCompile returns nil on a bad pattern
- Moves a MustCompile from a package-level var into a request path