メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#80026active

flag: add func (FlagSet) All() iter.Seq2\\[\\*Flag, bool]

新規提案

要約

AIによる要約であり、誤りを含む場合があります。

概要

flagパッケージにFlagSet.All() iter.Seq2[*Flag, bool]メソッドを追加し、全フラグを名前順に反復処理できるモダンなGo iteratorを提供する提案。

ステータス変更

(新規)active
2026-06-24に開催されたProposal Review Meeting(@adonovan、@bradfitz、@griesemer、@ianlancetaylor等が参加)でactiveカラムに追加された。まだ「discussion ongoing」ではなく「added to minutes」という状態で、正式な議論の俎上に載ったばかりの段階。

技術的背景

現状の問題点

flagパッケージにはVisitVisitAllという2つのコールバック形式のイテレータメソッドが存在する。これらはGo 1.22以降で導入されたrange funciter.Seq/iter.Seq2)と互換性がなく、使いにくいパターンを生む。
典型的なコードでは、エラー検出や早期終了のためにクロージャ外部の変数を使うという迂回策が必要になる。

// Before: 従来の書き方(コールバック方式)
var err error
cmdFlags.Visit(func(f *flag.Flag) {
    if globalFlags.Lookup(f.Name) == nil {
        err = tool.CommandLineErrorf("flag -%s belongs to subcommand but is placed before it", f.Name)
    }
})
if err != nil {
    return nil, nil, nil, err
}

また、内部実装としてFlagSetは「全フラグのmap(formal)」と「設定済みフラグのmap(actual)」を別々に保持しているため、VisitVisitAllの両方をカバーする単一APIを外部から効率的に作ることが困難だった。

提案された解決策

package flag
// All returns the sequence of all flags, in name order,
// paired with a boolean indicating whether the flag is set.
func (f *FlagSet) All() iter.Seq2[*Flag, bool]

iter.Seq2[*Flag, bool]として全フラグを返し、第2要素のboolでそのフラグが実際に設定されたかどうかを示す。これにより、VisitAll(全フラグ列挙)とVisit(設定済みフラグのみ)の両方のユースケースを1つのAPIで賄える。

これによって何ができるようになるか

コード例

// After: 新APIを使った書き方
for f, set := range cmdFlags.All() {
    if set && globalFlags.Lookup(f.Name) == nil {
        return nil, nil, nil, tool.CommandLineErrorf("flag -%s belongs to subcommand but is placed before it", f.Name)
    }
}

ユースケース例:

  1. 設定済みフラグのフィルタリング: set == trueの条件を加えるだけで、設定されたフラグのみを対象に処理できる
  2. 環境変数による未設定フラグの補完: set == falseのフラグを見つけて、対応する環境変数から値を自動設定する処理が自然に書ける
  3. サブコマンド前に置かれたフラグの検出: 条件付き早期リターンがbreakで素直に書ける(従来はクロージャ外変数が必要だった)

議論のハイライト

  • AllSet() iter.Seq[*Flag]の追加案は却下: jimmyfrascheAllSetメソッドの追加を提案したが、adonovanは「boolを第2要素として返すことで設定済みフラグの絞り込みも可能なため不要」と判断
  • 内部実装の懸念: mvdanは「formalactualの2つのmapを持つ現在の実装ではiter.Seq2を返すためにmap間のlookupが必要」と指摘したが、earthboundkidは「f.formalをループしてf.actual[name]でlookupするだけで十分効率的」と反論
  • 重複proposalとの統合: 同趣旨の#65675(@earthboundkidが提案)が2024年2月から存在していたが見落とされており、#80026がproposal evaluation boardに載ったため#65675がクローズされた
  • 提案のAPI設計: 2024年11月に#65675でも同一のAPI(iter.Seq2[*Flag, bool])が独立して提案されていた点で設計への収束が見られる
  • 実装意欲: earthboundkidが承認された場合に実装を担当する意向を表明している

関連リンク