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

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

このコミットは、Go言語の標準ライブラリioパッケージにおけるCloserインターフェースのCloseメソッドに関するドキュメントを更新するものです。具体的には、Closeメソッドが複数回呼び出された場合の動作が「未定義」であることを明示的に追記し、開発者に対してその挙動が実装依存であることを明確に伝えています。

コミット

commit 8691f90cc53f1ebcd27d82c71432ca873a3744ca
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Tue Apr 9 10:17:35 2013 -0700

    io: document non-guarantees of io.Closer
    
    R=r, golang-dev
    CC=golang-dev
    https://golang.org/cl/8575043

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

https://github.com/golang/go/commit/8691f90cc53f1ebcd27d82c71432ca873a3744ca

元コミット内容

io: document non-guarantees of io.Closer
    
R=r, golang-dev
CC=golang-dev
https://golang.org/cl/8575043

変更の背景

Go言語のio.Closerインターフェースは、リソースを解放するためのClose()メソッドを定義しています。しかし、このClose()メソッドが一度呼び出された後に再度呼び出された場合の動作については、以前のドキュメントでは明確な規定がありませんでした。これにより、開発者はClose()を複数回呼び出した際の挙動について誤解したり、実装に依存した不安定なコードを書いてしまう可能性がありました。

このコミットは、このような曖昧さを解消し、Close()メソッドの複数回呼び出しに対する動作がio.Closerインターフェース自体によって保証されるものではなく、「未定義」であることを明示するために行われました。これにより、開発者は各Closerの実装が独自にその挙動を定義する必要があることを認識し、より堅牢なコードを書くことが期待されます。例えば、ファイルディスクリプタやネットワーク接続など、一度閉じたら再利用できないリソースを扱う際に、二重クローズによるエラーやパニックを防ぐための注意喚起となります。

前提知識の解説

Go言語のインターフェース (interface)

Go言語におけるインターフェースは、メソッドのシグネチャの集合を定義する型です。特定のインターフェースを実装する型は、そのインターフェースが定義するすべてのメソッドを持っている必要があります。Goのインターフェースは「暗黙的」であり、型がインターフェースのすべてのメソッドを実装していれば、明示的に「このインターフェースを実装する」と宣言する必要はありません。

io.Closerインターフェースは、以下のように定義されています。

type Closer interface {
    Close() error
}

これは、Close() errorというメソッドを持つ任意の型がio.Closerインターフェースを満たすことを意味します。ファイル、ネットワーク接続、データベース接続など、多くのリソースがこのインターフェースを実装し、使用後にリソースを適切に解放するためにClose()メソッドが呼び出されます。

未定義動作 (Undefined Behavior)

プログラミングにおける「未定義動作 (Undefined Behavior, UB)」とは、プログラミング言語の仕様が特定の状況下でのプログラムの挙動を規定していない状態を指します。未定義動作が発生した場合、コンパイラや実行環境はどのような動作をしてもよいとされており、予測不能な結果を招く可能性があります。例えば、プログラムがクラッシュしたり、誤った結果を返したり、セキュリティ上の脆弱性を引き起こしたりすることがあります。

io.CloserCloseメソッドが複数回呼び出された場合の動作が「未定義」であると明記されたことは、Go言語の仕様として、その挙動を保証しないことを意味します。これは、特定のCloserの実装が複数回呼び出しを許容する(例: 2回目以降の呼び出しでエラーを返す、何もしない)こともあれば、許容しない(例: パニックを起こす、不正な状態になる)こともある、ということを示唆しています。

技術的詳細

このコミットの技術的なポイントは、io.Closerインターフェースのドキュメントに、Closeメソッドの複数回呼び出しに関する「未定義動作」の概念を導入した点です。

以前のドキュメントでは、Closeメソッドの役割はリソースの解放であるとされていましたが、一度閉じられたリソースに対して再度Closeが呼び出された場合の挙動については言及がありませんでした。これにより、開発者は以下のような疑問や問題に直面する可能性がありました。

  1. 冪等性 (Idempotency) の欠如: 多くの開発者は、Closeメソッドが冪等である(複数回呼び出しても初回と同じ結果になる、または副作用がない)ことを期待しがちです。しかし、インターフェースの定義上、それは保証されていませんでした。
  2. 実装依存の挙動: os.FileClose()のように、2回目以降の呼び出しでos.ErrClosedのようなエラーを返す実装もあれば、net/httpパッケージのhttp.Response.Body.Close()のように、2回目以降の呼び出しで特に何もせず成功を返す実装もあります。また、中にはパニックを引き起こすような実装も考えられます。
  3. リソースリークや競合状態: Closeが冪等でない場合、複数回呼び出しによってリソースが不正な状態になったり、並行処理環境で競合状態が発生したりするリスクがありました。

このコミットによって追加されたドキュメントは、これらの問題を明確化します。

// The behavior of Close after the first call is undefined.
// Specific implementations may document their own behavior.

この記述は、以下の重要なメッセージを伝えています。

  • インターフェースレベルでの保証なし: io.Closerインターフェース自体は、Closeの複数回呼び出しに対する特定の挙動を保証しません。
  • 実装者への責任: Closerインターフェースを実装する型(例えば、os.Filenet.Connなど)は、自身のCloseメソッドが複数回呼び出された場合の挙動を独自に定義し、必要であればドキュメント化する責任があります。
  • 利用者への注意喚起: io.Closerを利用する開発者は、特定のCloser実装のドキュメントを確認し、Closeの複数回呼び出しが安全であるかどうかを判断する必要があります。一般的には、Closeは一度だけ呼び出すのが最も安全なプラクティスとされます。

これにより、Go言語のAPI設計における「最小限の保証」という原則がより明確になり、開発者はより注意深くリソースのライフサイクルを管理するよう促されます。

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

--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -70,6 +70,9 @@ type Writer interface {
 }
 
 // Closer is the interface that wraps the basic Close method.
+//
+// The behavior of Close after the first call is undefined.
+// Specific implementations may document their own behavior.
 type Closer interface {
 	Close() error
 }

コアとなるコードの解説

変更はsrc/pkg/io/io.goファイル内のCloserインターフェースの定義部分に集中しています。具体的には、Closerインターフェースのコメントブロックに以下の3行が追加されました。

// The behavior of Close after the first call is undefined.
// Specific implementations may document their own behavior.
  1. // The behavior of Close after the first call is undefined.
    • この行は、Closeメソッドが一度呼び出された後、再度呼び出された場合の動作が「未定義」であることを明確に宣言しています。これは、Go言語の仕様として、その挙動を保証しないことを意味します。
  2. // Specific implementations may document their own behavior.
    • この行は、各Closerの実装(例: os.Filenet.Connなど)が、自身のCloseメソッドが複数回呼び出された場合の具体的な挙動を独自に定義し、それをドキュメント化する責任があることを示唆しています。これにより、利用者は個々の実装のドキュメントを参照することで、そのCloseメソッドの複数回呼び出しに対する安全性を確認できるようになります。

これらの追加により、io.Closerインターフェースの契約がより厳密になり、開発者はリソースのクローズ処理を実装および利用する際に、より正確な情報に基づいて判断できるようになりました。

関連リンク

参考にした情報源リンク

  • https://go.dev/cl/8575043 (Go Gerrit Change-Id)
  • https://go.dev/src/io/io.go (Go ioパッケージのソースコード)
  • Web search results for "Go io.Closer Close method multiple calls undefined behavior" (検索結果のURLは省略し、内容を統合して記述)
    • go.dev
    • zwlin.io
    • google.com
    • stackoverflow.com
    • victoriametrics.com
    • antonz.org