go/types: add Scope.Elements iterator
要約
概要
go/typesパッケージのScope型に、スコープ内のオブジェクト(識別子に束縛された変数・関数・型など)を名前順に走査するイテレータメソッドを追加する提案です。Go 1.23で導入されたrange-over-func(イテレータ)の仕組みを活用し、既存のボイラープレートなループを簡潔に書けるようにします。
ステータス変更
active → likely_accept
2026-09-09のproposal review meetingにて、設計上の論点(戻り値の型、順序保証、命名)がおおむね解消されたことから「likely accept(承認見込み)」と判定されました。同時に最終コメント募集期間(Final Comment Period)に入っています。既にメソッド名をObjectsに変更する形で実装CLも提出されており、大きな異論がなければそのまま承認される見通しです。
技術的背景
現状の問題点
go/types.Scopeから要素を取得するには、現状Names()で名前のスライスを取得し、Lookup(name)で個々のオブジェクトを引く、という2段階の手順が必要です。
for _, name := range scope.Names() {
obj := scope.Lookup(name)
// objを使った処理
}
提案者(@adonovan)の調査によると、この定型パターンはx/toolsリポジトリだけでも29箇所以上に出現しており、冗長で書き間違えやすいコードになっています。
提案された解決策
Scopeにiter.Seq[Object]を返すイテレータメソッドを追加します。当初はElements()という名前で提案されましたが、レビュー中に「Object型自体が名前を保持しているためiter.Seq2[string, Object]にする必要はない」「ドキュメントでObjectsという表現も使われており、戻り値の型Objectとも呼称が一致する」という指摘を受け、最終的に**Objects()**という名前に変更されました。
package types // "go/types"
// Elements returns an iterator over the objects in the s in name order.
func (s *Scope) Objects() iter.Seq[Object] {
// Implementation note: Rearrange the data structure so we don't
// have to allocate and sort on every call.
names := s.Names()
return func(yield func(obj Object) bool) {
for _, name := range names {
if !yield(s.Lookup(name)) {
break
}
}
}
}
要素の走査順はNames()と同じ名前順(アルファベット順)で確定的であることが合意されました。ただし現状の実装は呼び出しのたびに名前一覧を割り当ててソートしており性能上のボトルネックになり得るため、実装CL(go.dev/cl/830324)ではScope.Names()の結果をキャッシュする最適化も同時に行われています。
これによって何ができるようになるか
go/typesを使った静的解析ツール(vet、gopls、コンパイラのフロントエンド、リンター、コード生成ツールなど)で、スコープ内の宣言済みオブジェクト(変数・定数・関数・型)を1行のfor-rangeループで列挙できるようになります。特にx/toolsやgoplsのようにScopeを頻繁に走査するツール群でコードの見通しが大幅に改善されます。
コード例
// Before: 従来の書き方
for _, name := range scope.Names() {
obj := scope.Lookup(name)
if _, ok := obj.(*types.TypeName); ok {
// 型名のみ処理
}
}
// After: 新しいイテレータを使った書き方
for obj := range scope.Objects() {
if _, ok := obj.(*types.TypeName); ok {
// 型名のみ処理
}
}
議論のハイライト
- ループ中の変更の扱い: @mateusz834から「ループ中にスコープが変更された場合の挙動を規定すべきでは」という懸念が出され、提案者の@adonovanは「変更を禁止し、末尾で要素数が変化していればパニックさせる」という方針を示しました。
- 戻り値の型論争: レビューで
iter.Seq2[string, Object](名前とオブジェクトのペア)にすべきかが議論されましたが、Object型自体が名前を保持しているため、単純なiter.Seq[Object]で十分と結論付けられました。 - 順序保証:
Scope.Children()(子スコープを列挙する既存のイテレータ)との順序の整合性について@neildから疑問が出ましたが、Childrenはスコープの階層、Objectsは名前空間という異なる領域を扱うため直接の対応関係はないと@aclementsが整理し、Names()と同じ名前順でよいことで合意しました。 - 性能面の懸念: 提案本文の時点から、
Names()ベースの実装は毎回スライス割り当てとソートが発生するため性能への影響が懸念されており、レビューでも「遅延ソート+キャッシュ」など複数の最適化案が検討されました。最終的に実装CLではScope.Names()の結果をキャッシュする改善も併せて行われています。 - メソッド名の変更: レビュー終盤で@aclementsが「
ElementsとObjectsのどちらが適切か」を問い、型Objectとの一貫性から最終的にObjectsへと名称変更されました。