Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11754] ファイルの概要

このコミットは、Go言語の標準ライブラリであるregexpパッケージ内のFindStringIndexおよびFindReaderIndex関数の戻り値に、名前付き戻り値パラメータlocを追加する変更です。これにより、これらの関数のドキュメントとの整合性が向上し、コードの可読性がわずかに向上します。

コミット

commit 9cd4a0467ae2494b0734693b90d47c604610a9ac
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Fri Feb 10 10:22:01 2012 +1100

    regexp: name result parameters referenced from docs
    
    Fixes #2953
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5653051

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/9cd4a0467ae2494b0734693b90d47c604610a9ac

元コミット内容

regexp: name result parameters referenced from docs

Fixes #2953

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5653051

変更の背景

この変更は、Go言語のIssue #2953「regexp: document FindStringIndex's return value as loc」に対応するものです。このIssueでは、regexpパッケージのFindStringIndex関数のドキュメントが、戻り値の[]intスライスをlocという名前で参照しているにもかかわらず、実際の関数シグネチャでは名前付き戻り値パラメータが使用されていない点が指摘されていました。

ドキュメントとコードの間のこの不一致は、開発者がドキュメントを読んだ際に、戻り値が具体的に何を意味するのか、あるいはどのように参照されるべきかについて混乱を招く可能性がありました。特に、loc[0]loc[1]がそれぞれマッチの開始と終了インデックスを示すという説明があるため、locという名前がコード上でも明示されている方が、ドキュメントの意図がより明確に伝わります。

このコミットは、このドキュメントとコードの間の整合性の問題を解決し、コードの可読性とドキュメントの正確性を向上させることを目的としています。

前提知識の解説

Go言語のregexpパッケージ

Go言語のregexpパッケージは、正規表現を扱うための標準ライブラリです。Perl、Python、その他の言語で一般的に使用される正規表現の構文とセマンティクスをサポートしています。このパッケージは、文字列内でのパターンマッチング、部分文字列の検索、置換などの機能を提供します。

  • Regexp: コンパイルされた正規表現を表す型です。
  • FindStringIndex(s string) []int: 指定された文字列s内で正規表現に最初にマッチする部分の開始インデックスと終了インデックスを[]intスライスとして返します。マッチが見つからない場合はnilを返します。戻り値の[]intスライスは、[開始インデックス, 終了インデックス]の形式です。
  • FindReaderIndex(r io.RuneReader) []int: io.RuneReaderから読み取ったテキスト内で正規表現に最初にマッチする部分の開始インデックスと終了インデックスを[]intスライスとして返します。マッチが見つからない場合はnilを返します。

Go言語の名前付き戻り値パラメータ

Go言語では、関数の戻り値に名前を付けることができます。これを「名前付き戻り値パラメータ (Named Return Parameters)」と呼びます。

通常の戻り値の宣言:

func add(a, b int) int {
    return a + b
}

名前付き戻り値パラメータの宣言:

func add(a, b int) (sum int) {
    sum = a + b
    return // sum が自動的に返される
}

名前付き戻り値パラメータを使用する利点は以下の通りです。

  1. 可読性の向上: 戻り値が何を意味するのかが明確になります。特に複数の戻り値がある場合に有効です。
  2. ドキュメントとの整合性: ドキュメントで特定の名前で参照されている戻り値を、コード上でもその名前で明示できます。
  3. deferとの連携: defer文内で名前付き戻り値パラメータを変更することで、関数の終了直前に戻り値を操作できます。
  4. ゼロ値の初期化: 名前付き戻り値パラメータは、関数が開始される際にその型のゼロ値で自動的に初期化されます。

このコミットでは、FindStringIndexFindReaderIndexの戻り値がドキュメントでlocとして参照されているため、コード上でもlocという名前を明示することで、ドキュメントとの整合性を高めています。

技術的詳細

このコミットの技術的な変更は非常にシンプルですが、Go言語の設計思想とドキュメンテーションの重要性を示しています。

変更の核心は、regexpパッケージ内の2つの関数、FindStringIndexFindReaderIndexの関数シグネチャに名前付き戻り値パラメータlocを追加することです。

変更前:

func (re *Regexp) FindStringIndex(s string) []int {
    // ...
    return nil
}

func (re *Regexp) FindReaderIndex(r io.RuneReader) []int {
    // ...
    return nil
}

変更後:

func (re *Regexp) FindStringIndex(s string) (loc []int) {
    // ...
    return nil // または return loc
}

func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) {
    // ...
    return nil // または return loc
}

この変更により、関数内部でlocという変数が自動的に宣言され、[]int型のゼロ値(nilスライス)で初期化されます。関数本体のロジックは、以前と同様にre.doExecuteの結果をaに代入し、そのaを返しています。Go言語では、名前付き戻り値パラメータが宣言されている場合、returnステートメントで明示的に値を指定しない場合(裸のreturn)、名前付き戻り値パラメータの現在の値が返されます。しかし、このケースではaを直接返しているため、loc = a; returnと書くのと同じ効果になります。

重要なのは、この変更が関数の外部からの呼び出し方や動作に影響を与えないことです。呼び出し元は引き続き[]intスライスを受け取ります。しかし、ドキュメントでlocという名前で説明されている戻り値が、コード上でもその名前で明示されることで、ドキュメントとコードの間のギャップが埋まり、開発者がドキュメントを読んだ際の理解が深まります。

これは、Go言語の「自己文書化コード」の原則を強化する小さな一歩とも言えます。コード自体がその意図をより明確に伝えることで、外部ドキュメントへの依存を減らし、メンテナンス性を向上させます。

コアとなるコードの変更箇所

変更はsrc/pkg/regexp/regexp.goファイルにあります。

--- a/src/pkg/regexp/regexp.go
+++ b/src/pkg/regexp/regexp.go
@@ -676,7 +676,7 @@ func (re *Regexp) FindString(s string) string {
 // location of the leftmost match in s of the regular expression.  The match
 // itself is at s[loc[0]:loc[1]].
 // A return value of nil indicates no match.
-func (re *Regexp) FindStringIndex(s string) []int {
+func (re *Regexp) FindStringIndex(s string) (loc []int) {
  	a := re.doExecute(nil, nil, s, 0, 2)
  	if a == nil {
  		return nil
@@ -688,7 +688,7 @@ func (re *Regexp) FindStringIndex(s string) []int {
 // location of the leftmost match of the regular expression in text read from
 // the RuneReader.  The match itself is at s[loc[0]:loc[1]].  A return
 // value of nil indicates no match.
-func (re *Regexp) FindReaderIndex(r io.RuneReader) []int {
+func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) {
  	a := re.doExecute(r, nil, "", 0, 2)
  	if a == nil {
  		return nil

コアとなるコードの解説

上記のdiffを見ると、以下の2つの関数のシグネチャが変更されていることがわかります。

  1. func (re *Regexp) FindStringIndex(s string) []int

    • 変更前は、戻り値の型[]intのみが指定されていました。
    • 変更後は、func (re *Regexp) FindStringIndex(s string) (loc []int) となり、戻り値の[]intスライスにlocという名前が付けられました。
  2. func (re *Regexp) FindReaderIndex(r io.RuneReader) []int

    • 変更前は、戻り値の型[]intのみが指定されていました。
    • 変更後は、func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) となり、戻り値の[]intスライスにlocという名前が付けられました。

関数本体のロジック(re.doExecuteの呼び出しやnilチェック、aの返却)には一切変更がありません。これは、名前付き戻り値パラメータが導入されても、関数内部でその名前の変数を明示的に使用する必要はなく、最終的にreturnステートメントで返される値がその名前付きパラメータに割り当てられるためです。この場合、alocに割り当てられて返されます。

この変更は、コードの機能的な動作には影響を与えず、主にドキュメントとの整合性とコードの自己文書化能力を向上させるためのものです。ドキュメントで「loc[0]loc[1]」と説明されている部分が、コード上でもlocという名前で明確に示されることで、開発者がドキュメントとコードを行き来する際の理解がスムーズになります。

関連リンク

参考にした情報源リンク