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.
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.
Get my free newsletter every second week
Used by 500+ developers to boost their Go skills.
"I'll share tips, interesting links and new content. You'll also get a brief guide to time for developers for free."