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

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

このコミットは、Go言語の公式ドキュメント「Effective Go」に「Redeclaration(再宣言)」に関する新しいセクションを追加するものです。具体的には、:=(ショート変数宣言)がどのように既存の変数と新しい変数を同時に扱うか、特にエラーハンドリングの文脈でどのように機能するかを詳細に説明しています。

コミット

commit a41006f35a5931387a3111739e4c97ff3568bbcc
Author: Rob Pike <r@golang.org>
Date:   Tue Dec 20 14:15:35 2011 -0800

    effective_go: redeclaration
    
    Fixes #2455.
    Fixes #2013.
    
    R=rsc, r, gri
    CC=golang-dev
    https://golang.org/cl/5498053

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

https://github.com/golang/go/commit/a41006f35a5931387a3111739e4c97ff3568bbcc

元コミット内容

このコミットは、doc/effective_go.html および doc/effective_go.tmpl ファイルに、Go言語のショート変数宣言(:=)における変数の「再宣言」に関する新しいセクションを追加しています。このセクションは、特にerr変数の再利用がどのように行われるか、そしてその背後にあるGo言語の設計思想(実用主義)を説明しています。

変更の背景

この変更は、Go言語のショート変数宣言(:=)の挙動、特に既存の変数と新しい変数が混在する場合の「再宣言」のルールについて、ユーザーが混乱する可能性があったため、公式ドキュメントで明確な説明を提供することを目的としています。コミットメッセージにある Fixes #2455Fixes #2013 は、この機能に関する既存の課題や議論があったことを示唆しています。

Go言語では、エラーハンドリングのパターンとして、複数の関数呼び出しで同じerr変数を再利用することが一般的です。しかし、:=演算子は通常、新しい変数を宣言するために使用されます。この二つの概念が衝突する可能性があるため、Go言語は特定の条件下で既存の変数を「再宣言」ではなく「再代入」として扱う特別なルールを導入しています。このコミットは、その「特別なルール」を公式ドキュメントに明記することで、開発者の理解を深め、混乱を解消しようとしています。

前提知識の解説

Go言語の変数宣言

Go言語には、変数を宣言する方法がいくつかあります。

  1. varキーワードによる宣言:

    var name string = "Go"
    var age int
    

    これは明示的な型指定と初期化を行う方法です。

  2. ショート変数宣言(:=:

    name := "Go" // 型推論により string 型となる
    age := 10    // 型推論により int 型となる
    

    これは関数内でのみ使用でき、型推論が行われるため、より簡潔に記述できます。

Go言語のエラーハンドリング

Go言語では、エラーは戻り値として扱われることが一般的です。慣習として、関数の最後の戻り値はerror型であることが多く、エラーが発生しなかった場合はnilが返されます。

func doSomething() (result string, err error) {
    // ...
    if someCondition {
        return "", errors.New("something went wrong")
    }
    return "success", nil
}

このエラーハンドリングのパターンでは、複数の操作を行う際に同じerr変数を繰り返し使用することがよくあります。

f, err := os.Open("file.txt")
if err != nil {
    return err
}
defer f.Close()

data, err := ioutil.ReadAll(f) // ここでも err を再利用
if err != nil {
    return err
}

スコープ

Go言語におけるスコープは、変数がアクセス可能な範囲を定義します。変数は宣言されたブロック({}で囲まれた範囲)内で有効です。外側のスコープで宣言された変数は、内側のスコープからアクセスできますが、内側のスコープで同じ名前の変数を宣言すると、それは新しい変数となり、外側の変数を「シャドウ」します。

技術的詳細

このコミットで追加された「Redeclaration」セクションは、:=ショート変数宣言の特別な挙動について説明しています。通常、:=は新しい変数を宣言しますが、特定の条件下では、既に宣言されている変数を「再代入」しつつ、同時に新しい変数を宣言することができます。

その条件は以下の3つです。

  1. 同じスコープ内での宣言: vという変数が既に宣言されている場合、この:=宣言がvの既存の宣言と同じスコープ内にある必要があります。もしvが外側のスコープで宣言されている場合、:=宣言は新しいv変数を(内側のスコープで)作成し、外側のvをシャドウします。

  2. 初期化値の代入可能性: 対応する初期化値が、既存のv変数に代入可能である必要があります。

  3. 少なくとも1つの新しい変数の宣言: この:=宣言において、少なくとも1つの新しい変数が宣言されている必要があります。つまり、全ての変数が既存のものである場合は、:=は使用できず、通常の代入(=)を使用する必要があります。

このルールは、特にGo言語で頻繁に登場するエラーハンドリングのパターンにおいて、err変数を繰り返し使用する際に非常に実用的です。これにより、冗長なvar err errorの宣言を避けることができ、コードをより簡潔に保つことができます。

例:

f, err := os.Open(name) // f と err を新規宣言
if err != nil {
    // エラー処理
}

d, err := f.Stat() // d を新規宣言し、既存の err に再代入
if err != nil {
    // エラー処理
}

この挙動は、Go言語の「実用主義(pragmatism)」の哲学に基づいています。コードの可読性と簡潔さを向上させるために、言語仕様に特別な例外が設けられているのです。

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

このコミットによるコードの変更は、Go言語のドキュメントファイルである doc/effective_go.htmldoc/effective_go.tmpl への追加のみです。Go言語のコンパイラやランタイムの挙動を変更するものではありません。

具体的には、doc/effective_go.htmldoc/effective_go.tmpl の両ファイルに、以下のHTMLコンテンツが追加されています。

<h3 id="redeclaration">Redeclaration</h3>

<p>
An aside: The last example in the previous section demonstrates a detail of how the
<code>:=</code> short declaration form works.
The declaration that calls <code>os.Open</code> reads,
</p>

<pre>
f, err := os.Open(name)
</pre>

<p>
This statement declares two variables, <code>f</code> and <code>err</code>.
A few lines later, the call to <code>f.Stat</code> reads,
</p>

<pre>
d, err := f.Stat()
</pre>

<p>
which looks as if it declares <code>d</code> and <code>err</code>.
Notice, though, that <code>err</code> appears in both statements.
This duplication is legal: <code>err</code> is declared by the first statement,
but only <em>re-assigned</em> in the second.
This means that the call to <code>f.Stat</code> uses the existing
<code>err</code> variable declared above, and just gives it a new value.
</p>

<p>
In a <code>:=</code> declaration a variable <code>v</code> may appear even
if it has already been declared, provided:
</p>

<ul>
<li>this declaration is in the same scope as the existing declaration of <code>v</code>
(if <code>v</code> is already declared in an outer scope, the declaration will create a new variable),</li>
<li>the corresponding value in the initialization is assignable to <code>v</code>, and</li>
<li>there is at least one other variable in the declaration that is being declared anew.</li>
</ul>

<p>
This unusual property is pure pragmatism,
making it easy to use a single <code>err</code> value, for example,
in a long <code>if-else</code> chain.
You'll see it used often.
</p>

コアとなるコードの解説

追加されたHTMLコンテンツは、Go言語の:=ショート変数宣言における「再宣言」のルールを、具体的なコード例を交えて説明しています。

  1. 導入: :=の挙動に関する詳細を説明することを目的としていることを示します。
  2. f, err := os.Open(name)の例: 最初の:=宣言でferrが新しく宣言されることを示します。
  3. d, err := f.Stat()の例: 2番目の:=宣言でdが新しく宣言され、errは既存のものが再代入されることを強調します。
  4. 再宣言の合法性: errが両方のステートメントに現れることが合法であり、最初の宣言でerrが宣言され、2番目の宣言では「再代入」されるだけであることを明確にします。
  5. 再宣言の3つの条件: :=宣言で既存の変数vが再利用されるための厳密な3つの条件を箇条書きで示します。
    • 同じスコープ内であること。
    • 初期化値が代入可能であること。
    • 少なくとも1つの新しい変数が宣言されていること。
  6. 実用主義: この「珍しい」特性が、if-elseチェーンでのerr値の単一利用を容易にするための「純粋な実用主義」に基づいていることを説明し、このパターンが頻繁に使用されることを示唆しています。

このドキュメントの追加により、Go言語の初心者や、この特定の挙動に疑問を持つ開発者に対して、公式かつ明確な説明が提供されることになります。

関連リンク

参考にした情報源リンク