If you’re hardcoding data in your code, you as the developer are responsible for correctness. In other words: Invalid hardcoded data is usually a programmer error.
In my opinion it’s valid to panic on these kind of errors. Your program can’t do much about them but shutdown. It’s similar to attempting to access a slice element past the length of the slice.
The must convention
In Go there is the following convention: Functions or methods that are prefixed with Must (or must)
will panic instead of returning an error. Apart from that they do the same thing as the function without the Must prefix.
For example, suppose you have the following function:
func ParseXYZ(coords string) ([3]int, error) {
//... skipped for brevity.
}
The Must function will look like this:
func MustParseXYZ(coords string) [3]int {
xyz, err := ParseXYZ(coords)
if err != nil {
panic(err)
}
return xyz
}
This pattern comes in handy when dealing with hardcoded data or test data. It enables you to quickly create
results without having to worry about error handling or littering your code with err != nil statements.
This pattern is used in the standard library regexp package. See regexp.Compile and regexp.MustCompile.
When not to use this
The Must functions should only be used with developer provided data. Data coming from other sources (like user input) should always use regular error handling.
A generic Must function
Since Go 1.18 we have access to generics, instead of writing manual Must* functions, we can create a Must function that works for all types.
This Must function will take the results of the target function as input and looks like this:
func Must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
You can then use it to simplify the initialization of complex test data:
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
coord1 := Must(ParseXYZ("1,2,3"))
coord2 := Must(ParseXYZ("-1,-2")) // missing one coordinate by design
fmt.Println(coord1)
fmt.Println(coord2)
}
func Must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
func ParseXYZ(coords string) ([3]int, error) {
elements := strings.Split(coords, ",")
if len(elements) != 2 {
return [3]int{}, fmt.Errorf("expected %d elements, got %d", 3, len(elements))
}
var xyz [3]int
for i, el := range elements {
nr, err := strconv.Atoi(el)
if err != nil {
return [3]int{}, fmt.Errorf("failed to parse %d element as a number: %w", i, err)
}
xyz[i] = nr
}
return xyz, nil
}
This Must function assumes that there will be a single result type and a single error returned. This is usually the case, but nothing is stopping you from adding Must2 or Must3 functions when necessary.
Career choice: Learn skills to mitigate the upcoming AI privacy disaster*
Join 800+ devs reading my newsletter
*Everyone and their mother is sending sensitive data to AI systems with little concern for their privacy. If you read the fineprint, vendors and platforms actually offer very little guarantees. It's a matter of time before it goes wrong.
From March 2026 onwards, I'll be writing about development of verifiably-secure services using OpenPCC.