In Go's regexp package, when should you use regexp.MustCompile instead of regexp.Compile?
answer
- who wrote the pattern?
- one returns an error, one panics
- the stdlib Must convention
- literal in source versus config file
- package-level var compiles before main
basics
~20 sregexp.MustCompile panics if the pattern is invalid and returns only a *regexp.Regexp, so it suits patterns written as literals in your source, normally as a package-level var. Use regexp.Compile, which returns an error, for any pattern supplied at runtime.
solid answer
~50 s`regexp.Compile(expr string) (*regexp.Regexp, error)` is the general constructor: it reports a bad pattern as an error you handle. `regexp.MustCompile(expr string) *regexp.Regexp` is the same thing with the error turned into a panic, following the stdlib `Must` convention. The rule of thumb is where the pattern comes from. If it is a string literal in your code, a failure is a programmer bug, not an input condition, so `MustCompile` is right — and the idiom is a package-level `var re = regexp.MustCompile(...)`, which compiles once during package initialisation, before `main` runs, so a typo blows up at process start rather than on the first request. If the pattern comes from a config file, a CLI flag, a database row or a request, use `Compile` and surface the error to whoever wrote the pattern. Never wrap `MustCompile` in `recover` to fake that.
code
go · 11 lines// Fixed in source: a bad pattern here is a bug, so panic at init.
var idRe = regexp.MustCompile(`^[a-z]+-[0-9]{4}$`)
// Supplied by an operator: a bad pattern is input, so report it.
func compileFilter(pattern string) (*regexp.Regexp, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, fmt.Errorf("invalid filter %q: %w", pattern, err)
}
return re, nil
}go deeper
Be ready to state both signatures from memory and say which one panics. Know that the everyday form for a fixed pattern is a package-level var initialised with regexp.MustCompile.
Explain that MustCompile is Compile plus a panic, that package-level initialisers run before main, and that compiling parses the pattern into a program every time it is called.
Show the judgment: patterns from config, flags or requests are input and must go through Compile with the error reported to the operator. Be able to argue why recovering around MustCompile is the wrong fix.
Frame it as a failure-domain decision for the codebase: which pattern sources are allowed to abort the process at start-up, and which must degrade to a rejected reload with the previous rules still serving traffic.
## The two constructors Go's `regexp` package gives you two ways to turn pattern text into a matcher: - `func Compile(expr string) (*Regexp, error)` — returns a compiled `*regexp.Regexp`, or `nil` plus an error describing what is wrong with the pattern. - `func MustCompile(expr string) *Regexp` — the same work, but a bad pattern panics instead of returning an error, so there is only one return value. `MustCompile` is literally `Compile` with the error path converted to a panic. There is no difference at match time: both hand back the same kind of compiled object, and neither is faster than the other once you have it. ## What "compiling" actually does Compiling is real work. The pattern text is parsed into a syntax tree (that is the `regexp/syntax` package), simplified, and then turned into a program the matching engine executes. That cost is paid once per `Compile`/`MustCompile` call, and it is typically far larger than the cost of matching one short string. That is why the whole leaf is about *reusing* the compiled value rather than rebuilding it. ## The choice is about where the pattern comes from The question to ask is: **can this pattern be wrong at runtime in a way that is not my bug?** - A pattern that is a string literal in your source cannot change after you ship. If it fails to parse, your code is broken, and you want to know immediately and loudly. `MustCompile` is correct. - A pattern that arrives from a config file, an environment variable, a command-line flag, an admin UI or a database row is *input*. Someone else can get it wrong, and taking the process down over their typo is a poor design. `Compile` is correct, and the error should be reported with enough context to fix the pattern. ## The package-level var idiom The conventional shape for a fixed pattern is: ```go var idRe = regexp.MustCompile(`^[a-z]+-[0-9]{4}$`) ``` This matters for three reasons: 1. **It compiles exactly once.** Package-level variable initialisers run during package initialisation, before `main`. Every later call reuses the same compiled `*regexp.Regexp`. 2. **Failure happens at start-up.** If the literal is malformed, the panic occurs as the program initialises, not deep inside a request handler an hour later. 3. **It is shareable.** One compiled `*regexp.Regexp` can be used by the whole program for its lifetime; its matching methods are safe to call from many goroutines at once, so you do not need a copy per caller. The same `Must` convention appears elsewhere in the standard library (for example `template.Must`), and it always means the same thing: "this input is baked into the program, so an error here is a bug." ## Handling the error from Compile properly ```go re, err := regexp.Compile(userPattern) if err != nil { return fmt.Errorf("invalid filter %q: %w", userPattern, err) } ``` On error, the returned `*Regexp` is `nil`; there is no partially usable matcher to fall back on. Check the error before touching the value or you will dereference a nil pointer. ## Anti-patterns interviewers listen for - **`MustCompile` on runtime input.** A single bad character in an operator's config now crashes the service. This is the most common real defect in this area. - **`defer recover()` around `MustCompile`** to "handle" a bad pattern. If you need error handling, you needed `Compile`. Recovering here hides the design mistake and leaves the surrounding code with no matcher. - **Compiling in the hot path.** Calling `MustCompile` inside a function that runs per request or per line pays the parse cost every time. Hoist it. - **Believing `MustCompile` is an optimisation.** It is not; the only difference is how a malformed pattern is reported. ## A useful mental split Treat a compiled `*regexp.Regexp` like any other expensive, immutable, reusable value: build it once as far up the lifetime as you can, then read from it many times. `MustCompile` is the version of that for values known when you write the code; `Compile` is the version for values known only when the program runs.
- Why is a package-level var the usual home for a MustCompile call?Package-level initialisers run during package initialisation, before `main`, so the pattern is compiled exactly once and a malformed literal panics at process start instead of inside the first request that reaches it. Every later call then reuses the one compiled `*regexp.Regexp`.
- A pattern comes from a config file that operators edit and the service hot-reloads. What breaks if you use regexp.MustCompile there?A single typo in the config panics the goroutine that reloads it and normally takes the process down, turning an operator mistake into an outage. Use `regexp.Compile`, reject the reload, keep the previously compiled rules in place, and report the error with the offending pattern quoted.
- If regexp.Compile returns an error, is the returned *regexp.Regexp still usable?No — it is `nil`. There is no degraded matcher to fall back on, so you must check the error before using the value, otherwise the first call on it is a nil-pointer dereference.
saying these in an interview costs you the question
- Claiming MustCompile matches faster than Compile
- Calling MustCompile on a pattern from config or a request
- Wrapping MustCompile in recover to handle bad patterns
- Using the Regexp returned alongside a non-nil Compile error
- Compiling the same literal pattern inside a per-request function