[インデックス 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` に対応)