must: Do
要約
概要
標準ライブラリに must.Do / must.Get のようなヘルパー関数を追加するプロポーザルです。regexp.MustCompile や template.Must と同様に、エラーが発生した場合にパニックを起こすことで、エラーを返す関数を式の文脈(変数初期化、グローバル変数定義など)で簡潔に利用できるようにすることを目的としています。
ステータス変更
hold → active
2026年1月28日、関連するジェネリクスメソッドの提案(#77273「spec: generic methods for Go」)の解決を待つために一時的に hold 状態に置かれました。#77273 が承認されたことで、(*testing.T).Must[T any](v T, err error) T のような形でテスト型にジェネリクスメソッドを追加できる見通しが立ちました。その後、2026年3月26日にコミュニティから hold 解除の要請があり、2026年4月8日のプロポーザルレビューミーティング(@aclements, @adonovan, @griesemer, @ianlancetaylor, @neild 参加)にて active 状態へ移行、引き続き議論が継続されています。
技術的背景
現状の問題点
Go 標準ライブラリには regexp.MustCompile や template.Must など、エラー時にパニックするパターンが散在しています。しかし、これらはパッケージごとに個別実装されており、汎用的な仕組みが存在しません。ジェネリクス以前はユーザーが型ごとに個別のラッパーを書く必要がありましたが、ジェネリクス導入後も標準ライブラリには統一的な手段が提供されていませんでした。
// 現状: グローバル変数の初期化に冗長なコードが必要
var baseURL *url.URL
func init() {
var err error
baseURL, err = url.Parse("https://example.com/api")
if err != nil {
panic(err)
}
}
提案された解決策
ジェネリクスを活用した汎用ヘルパー関数を標準ライブラリに追加します。現時点での議論では以下のシグネチャが有力です。
// Get: (T, error) からエラー時にパニックし、正常時に値を返す
func Get[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
// Do: error のみを受け取り、エラー時にパニックする(引数なし値返却版)
func Do(err error) {
if err != nil {
panic(err)
}
}
パッケージ名・関数名については、must.Get、must.Do、errors.Must、must.Value など複数の候補が議論中です。
これによって何ができるようになるか
コード例
// Before: init関数やグローバル変数での冗長なエラー処理
var proxyURL *url.URL
func init() {
var err error
proxyURL, err = url.Parse("https://proxy.example.com")
if err != nil {
panic(err)
}
}
// After: must.Get を使った簡潔な書き方(提案後)
var proxyURL = must.Get(url.Parse("https://proxy.example.com"))
// Before: JSON シリアライズでのエラー処理
data, err := json.Marshal(knownStruct)
if err != nil {
panic(err) // 実際には発生しないと分かっている
}
// After:
data := must.Get(json.Marshal(knownStruct))
実際のユースケースとして、Tailscale のコードベースでは must.Get が広く活用されています(Tailscale の tailscale.com/util/must パッケージ参照)。dottedmag 氏による 200KLoC の調査では、json.Marshal ラップが最多(18%)、次いで http.NewRequestWithContext(8%)、json.Unmarshal(7%)と続いており、エラーが実際には発生しえない前提のコードで幅広く使われています。
議論のハイライト
- パッケージ・関数名の議論(バイクシェッド):
must.Get、must.Do、errors.Must、must.Valueなど、名前について活発な議論が続いており、未決定です。errors.Mustは「エラーを生成する関数に聞こえる」として否定的な意見もあります。 must.Doの有用性への懐疑:Do(値を返さない版)は値を式の文脈から救い出す機能がないためGetより価値が低いという意見があります。一方で、bytes.Bufferへの書き込みのように「絶対に失敗しないはずだが念のため確認したい」用途には有用との反論もあります。- 誤用への懸念:
must.Do(err)が一般的なエラーハンドリングの代替として main 関数等で乱用される「魅力的な罠(attractive nuisance)」になる恐れがあるとして、@ianlancetaylor が懸念を示しました。 testing.Tへの追加の検討と制約: テストコードでの利便性から(*testing.T).Mustの追加も議論されましたが、Goのジェネリクスメソッド制約(型パラメータを持つメソッドを型に追加できない)により、testing.TBインターフェースに対応できない技術的課題がありました。#77273(generic methods)の承認により、この問題は解決される見込みです。ただし @adonovan はテストでの安易な利用によりエラーメッセージが貧弱になる懸念を示しています。- 新規パッケージ追加への慎重な姿勢: 関数1つのためだけに新しいパッケージ(
must)を追加することへの抵抗感も示されており、既存のerrorsパッケージへの追加案も浮上しています。