[インデックス 16582] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/cipher パッケージ内の StreamWriter 型に関する変更です。具体的には、StreamWriter の Close メソッドの挙動とドキュメントが改善されています。
コミット
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
このコミットは、StreamWriter の Close メソッドのドキュメントを更新し、その動作を変更します。具体的には、基になる io.Writer が io.Closer インターフェースを実装していない場合にパニックを起こさないように修正し、Close メソッドが何をするのか、そしてそれがバッファをフラッシュするものではないことを明確にするドキュメントを追加します。
変更の背景
Go言語の io パッケージには、データの読み書きを行うための基本的なインターフェースが定義されています。io.Writer はデータを書き込むためのインターフェースであり、io.Closer はリソースを解放するための Close メソッドを持つインターフェースです。
crypto/cipher パッケージの StreamWriter は、暗号ストリーム(cipher.Stream)を io.Writer にラップし、書き込まれるデータを暗号化(または復号化)する役割を担っています。元々の StreamWriter.Close メソッドの実装は、ラップしている io.Writer が io.Closer インターフェースを実装していることを前提としていました。もし io.Writer が io.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)xがT型でない場合、ランタイムパニックが発生します。 -
2つの戻り値 (comma-ok イディオム):
v, ok := x.(T)この形式では、okはxがT型である場合にtrue、そうでない場合にfalseとなります。xがT型でない場合でもパニックは発生せず、okがfalseになるため、安全に型チェックを行うことができます。
crypto/cipher パッケージ
crypto/cipher パッケージは、ストリーム暗号やブロック暗号のモード(CBC, CTRなど)を実装するための共通インターフェースを提供します。
-
cipher.Stream:type Stream interface { XORKeyStream(dst, src []byte) }ストリーム暗号のインターフェースです。
XORKeyStreamメソッドは、srcのバイト列とキーストリームをXOR演算し、結果をdstに書き込みます。暗号化と復号化の両方に使用できます。 -
StreamWriter:StreamWriterはcipher.Streamとio.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.W が io.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)) が使用されています。
c, ok := w.W.(io.Closer): これはw.Wがio.Closerインターフェースを実装しているかどうかを安全にチェックします。- もし
w.Wがio.Closerであれば、cにio.Closer型としてw.Wが代入され、okはtrueになります。 - もし
w.Wがio.Closerでなければ、cはnilになり、okはfalseになります。この場合、パニックは発生しません。
- もし
if c, ok := w.W.(io.Closer); ok:okがtrueの場合のみ、つまりw.Wがio.Closerを実装している場合にのみ、c.Close()が呼び出されます。return nil:w.Wがio.Closerを実装していない場合 (okがfalseの場合)、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メソッドの新しい挙動を正確に記述しています。基になるWriterがio.CloserであればそのCloseメソッドを呼び出し、そうでなければnilを返すことを明示しています。
これらの変更により、StreamWriter の Close メソッドはより安全で予測可能な挙動をするようになり、その役割もドキュメントによって明確になりました。
コアとなるコードの変更箇所
--- 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` に対応)