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

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

このコミットは、Go言語の標準ライブラリ crypto/cipher パッケージ内の StreamWriter 型に関する変更です。具体的には、StreamWriterClose メソッドの挙動とドキュメントが改善されています。

コミット

commit 14e52c74bc7492098ea630bc84514fb427ef8bd9
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Mon Jun 17 07:30:04 2013 -0700

    crypto/cipher: StreamWriter.Closer docs + behavior change
    
    Don't panic when the underlying Writer isn't a Closer. And
    document what Close does and clarify that it's not a Flush.
    
    R=golang-dev, agl
    CC=golang-dev
    https://golang.org/cl/10310043

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

https://github.com/golang/go/commit/14e52c74bc7492098ea630bc84514fb427ef8bd9

元コミット内容

crypto/cipher: StreamWriter.Closer docs + behavior change

このコミットは、StreamWriterClose メソッドのドキュメントを更新し、その動作を変更します。具体的には、基になる io.Writerio.Closer インターフェースを実装していない場合にパニックを起こさないように修正し、Close メソッドが何をするのか、そしてそれがバッファをフラッシュするものではないことを明確にするドキュメントを追加します。

変更の背景

Go言語の io パッケージには、データの読み書きを行うための基本的なインターフェースが定義されています。io.Writer はデータを書き込むためのインターフェースであり、io.Closer はリソースを解放するための Close メソッドを持つインターフェースです。

crypto/cipher パッケージの StreamWriter は、暗号ストリーム(cipher.Stream)を io.Writer にラップし、書き込まれるデータを暗号化(または復号化)する役割を担っています。元々の StreamWriter.Close メソッドの実装は、ラップしている io.Writerio.Closer インターフェースを実装していることを前提としていました。もし io.Writerio.Closer でない場合、型アサーション w.W.(io.Closer) がパニックを引き起こす可能性がありました。

この挙動は、StreamWriter を使用するプログラマにとって予期せぬパニックを引き起こす可能性があり、堅牢性に欠けていました。また、Close メソッドの役割がドキュメントで明確にされていなかったため、ユーザーが Close を呼び出すことで内部バッファがフラッシュされると誤解する可能性もありました。StreamWriter は内部バッファを持たないため、Close はフラッシュの機能を提供しません。

これらの問題を解決し、StreamWriter の使いやすさと堅牢性を向上させるために、このコミットが導入されました。

前提知識の解説

Go言語のインターフェース

Go言語のインターフェースは、メソッドのシグネチャの集合を定義します。型がインターフェースのすべてのメソッドを実装していれば、その型はそのインターフェースを満たします。Goのインターフェースは暗黙的に満たされるため、明示的な宣言は不要です。

  • io.Writer:

    type Writer interface {
        Write(p []byte) (n int, err error)
    }
    

    データを書き込むための基本的なインターフェースです。

  • io.Closer:

    type Closer interface {
        Close() error
    }
    

    リソースを解放するための Close メソッドを持つインターフェースです。ファイルハンドル、ネットワーク接続、データベース接続など、使用後に明示的に閉じる必要があるリソースによく使用されます。

型アサーション (.(type))

Go言語では、インターフェース型の変数が基となる具体的な型を保持しているかどうかをチェックし、その具体的な型に変換するために型アサーションを使用します。

構文は x.(T) のようになります。ここで x はインターフェース型の変数、T はアサートしたい型です。

  • 単一の戻り値: v := x.(T) xT 型でない場合、ランタイムパニックが発生します。

  • 2つの戻り値 (comma-ok イディオム): v, ok := x.(T) この形式では、okxT 型である場合に true、そうでない場合に false となります。xT 型でない場合でもパニックは発生せず、okfalse になるため、安全に型チェックを行うことができます。

crypto/cipher パッケージ

crypto/cipher パッケージは、ストリーム暗号やブロック暗号のモード(CBC, CTRなど)を実装するための共通インターフェースを提供します。

  • cipher.Stream:

    type Stream interface {
        XORKeyStream(dst, src []byte)
    }
    

    ストリーム暗号のインターフェースです。XORKeyStream メソッドは、src のバイト列とキーストリームをXOR演算し、結果を dst に書き込みます。暗号化と復号化の両方に使用できます。

  • StreamWriter: StreamWritercipher.Streamio.Writer を組み合わせて、io.Writer インターフェースを通じて暗号化されたデータを書き込めるようにするアダプターです。Write メソッドが呼び出されるたびに、内部で Stream.XORKeyStream を使用してデータを処理します。

技術的詳細

このコミットの主要な変更点は、StreamWriter.Close メソッドの堅牢性の向上と、そのドキュメントの明確化です。

変更前の StreamWriter.Close

変更前の StreamWriter.Close メソッドは以下のようになっていました。

func (w StreamWriter) Close() error {
	// This saves us from either requiring a WriteCloser or having a
	// StreamWriterCloser.
	return w.W.(io.Closer).Close()
}

この実装では、w.W (ラップされた io.Writer) が io.Closer インターフェースを実装していることを前提としています。もし w.Wio.Closer でない場合、w.W.(io.Closer) という型アサーションはランタイムパニックを引き起こします。これは、例えば bytes.Buffer のような io.Writer ではあるが io.Closer ではない型を StreamWriter に渡した場合に問題となります。

変更後の StreamWriter.Close

変更後の StreamWriter.Close メソッドは以下のようになります。

func (w StreamWriter) Close() error {
	if c, ok := w.W.(io.Closer); ok {
		return c.Close()
	}
	return nil
}

この変更では、Goの「comma-ok イディオム」 (value, ok := interfaceValue.(Type)) が使用されています。

  1. c, ok := w.W.(io.Closer): これは w.Wio.Closer インターフェースを実装しているかどうかを安全にチェックします。
    • もし w.Wio.Closer であれば、cio.Closer 型として w.W が代入され、oktrue になります。
    • もし w.Wio.Closer でなければ、cnil になり、okfalse になります。この場合、パニックは発生しません。
  2. if c, ok := w.W.(io.Closer); ok: oktrue の場合のみ、つまり w.Wio.Closer を実装している場合にのみ、c.Close() が呼び出されます。
  3. return nil: w.Wio.Closer を実装していない場合 (okfalse の場合)、Close メソッドはエラーなく nil を返します。これにより、不要なパニックが回避され、より堅牢な動作が保証されます。

ドキュメントの変更

StreamWriter の構造体コメントと Close メソッドのコメントも更新されています。

  • StreamWriter 構造体コメントへの追加: // A StreamWriter has no internal buffering; Close does not need // to be called to flush write data. この行は、StreamWriter が内部バッファを持たないこと、したがって Close を呼び出すことが書き込みデータをフラッシュする目的ではないことを明確にしています。これは、ユーザーが Close の役割を誤解するのを防ぎます。

  • Close メソッドコメントの更新: // Close closes the underlying Writer and returns its Close return value, if the Writer // is also an io.Closer. Otherwise it returns nil. このコメントは、Close メソッドの新しい挙動を正確に記述しています。基になる Writerio.Closer であればその Close メソッドを呼び出し、そうでなければ nil を返すことを明示しています。

これらの変更により、StreamWriterClose メソッドはより安全で予測可能な挙動をするようになり、その役割もドキュメントによって明確になりました。

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

--- a/src/pkg/crypto/cipher/io.go
+++ b/src/pkg/crypto/cipher/io.go
@@ -25,6 +25,8 @@ func (r StreamReader) Read(dst []byte) (n int, err error) {
 // StreamWriter wraps a Stream into an io.Writer. It calls XORKeyStream
 // to process each slice of data which passes through. If any Write call
 // returns short then the StreamWriter is out of sync and must be discarded.
+// A StreamWriter has no internal buffering; Close does not need
+// to be called to flush write data.
 type StreamWriter struct {
 	S   Stream
 	W   io.Writer
@@ -43,8 +45,11 @@ func (w StreamWriter) Write(src []byte) (n int, err error) {\n 	return\n }\n \n+// Close closes the underlying Writer and returns its Close return value, if the Writer
+// is also an io.Closer. Otherwise it returns nil.\n func (w StreamWriter) Close() error {\n-\t// This saves us from either requiring a WriteCloser or having a\n-\t// StreamWriterCloser.\n-\treturn w.W.(io.Closer).Close()\n+\tif c, ok := w.W.(io.Closer); ok {\n+\t\treturn c.Close()\n+\t}\n+\treturn nil\n }\n```

## コアとなるコードの解説

### `StreamWriter` 構造体コメントの変更

```diff
 // StreamWriter wraps a Stream into an io.Writer. It calls XORKeyStream
 // to process each slice of data which passes through. If any Write call
 // returns short then the StreamWriter is out of sync and must be discarded.
+// A StreamWriter has no internal buffering; Close does not need
+// to be called to flush write data.
 type StreamWriter struct {
 	S   Stream
 	W   io.Writer

StreamWriter の定義の直前に、新しいコメント行が追加されました。これは、StreamWriter が内部バッファを持たないこと、そして Close メソッドがデータのフラッシュを目的として呼び出される必要がないことを明確にしています。これにより、ユーザーが Close の意図を誤解するのを防ぎます。

Close メソッドの変更

+// Close closes the underlying Writer and returns its Close return value, if the Writer
+// is also an io.Closer. Otherwise it returns nil.\n func (w StreamWriter) Close() error {\n-\t// This saves us from either requiring a WriteCloser or having a\n-\t// StreamWriterCloser.\n-\treturn w.W.(io.Closer).Close()\n+\tif c, ok := w.W.(io.Closer); ok {\n+\t\treturn c.Close()\n+\t}\n+\treturn nil\n }\n```
`Close` メソッドのコメントが更新され、その新しい挙動が正確に記述されています。
そして、メソッドの実装が以下のように変更されました。

*   **変更前**: `return w.W.(io.Closer).Close()`
    これは、`w.W` が `io.Closer` であることを前提とした型アサーションであり、そうでない場合はパニックを引き起こしました。

*   **変更後**:
    ```go
    if c, ok := w.W.(io.Closer); ok {
        return c.Close()
    }
    return nil
    ```
    この変更により、`w.W` が `io.Closer` インターフェースを実装しているかどうかを安全にチェックする「comma-ok イディオム」が導入されました。
    *   `w.W` が `io.Closer` であれば、その `Close` メソッドが呼び出され、その結果が返されます。
    *   `w.W` が `io.Closer` でなければ、パニックを起こすことなく `nil` が返されます。

この修正により、`StreamWriter` はより堅牢になり、基になる `io.Writer` が `io.Closer` を実装しているかどうかにかかわらず、安全に `Close` メソッドを呼び出すことができるようになりました。

## 関連リンク

*   Go言語 `io` パッケージのドキュメント: [https://pkg.go.dev/io](https://pkg.go.dev/io)
*   Go言語 `crypto/cipher` パッケージのドキュメント: [https://pkg.go.dev/crypto/cipher](https://pkg.go.dev/crypto/cipher)
*   Go言語の型アサーションに関する公式ドキュメント: [https://go.dev/tour/methods/15](https://go.dev/tour/methods/15)

## 参考にした情報源リンク

*   Go言語の公式ドキュメント
*   Go言語のソースコード (特に `src/pkg/crypto/cipher/io.go`)
*   Go言語の型アサーションとインターフェースに関する一般的な知識
*   Go言語のコミット履歴 (GitHub)
*   Go言語のコードレビューシステム (Gerrit): [https://go-review.googlesource.com/c/go/+/10310043](https://go-review.googlesource.com/c/go/+/10310043) (コミットメッセージに記載されている `golang.org/cl/10310043` に対応)