[インデックス 17154] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/rand パッケージ内の ExampleRead 関数のサンプルコードを簡素化するものです。具体的には、io.ReadFull の特性をより適切に活用することで、不要な条件チェックを削除しています。
コミット
- コミットハッシュ:
ce00562607e0967ea8329aa4728a5bf4e1a8e666 - Author: Rob Pike r@golang.org
- Date: Mon Aug 12 12:52:23 2013 +1000
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ce00562607e0967ea8329aa4728a5bf4e1a8e666
元コミット内容
crypto/rand: simplify example to exploit properties of ReadFull
No need for the complex condition.
Fixes #6089
R=golang-dev, mischief, adg
CC=golang-dev
https://golang.org/cl/12731043
変更の背景
この変更の背景には、io.ReadFull 関数の挙動に対するより深い理解と、それに基づくコードの簡素化があります。元のコードでは、io.ReadFull の呼び出し後に、読み込んだバイト数 n が期待されるバイト数 len(b) と等しいかどうか、そしてエラー err が発生していないかどうかを両方チェックしていました。
しかし、io.ReadFull の設計上の特性として、この関数は「指定されたバイト数を正確に読み込む」ことを保証します。もし指定されたバイト数を読み込めなかった場合、io.ReadFull は必ず非nilのエラーを返します。したがって、n != len(b) という条件は、err != nil という条件が真である場合にのみ発生するため、冗長なチェックとなります。
この冗長性を排除し、よりGoらしい(idiomatic Go)エラーハンドリングにすることで、コードの可読性と簡潔性を向上させるのがこのコミットの目的です。また、この変更はIssue #6089を修正すると明記されています。
前提知識の解説
io.Reader インターフェース
Go言語における io.Reader は、データを読み込むための基本的なインターフェースです。
type Reader interface {
Read(p []byte) (n int, err error)
}
Read メソッドは、データを p に最大 len(p) バイトまで読み込み、読み込んだバイト数 n とエラー err を返します。エラーが io.EOF の場合、それ以上読み込むデータがないことを示します。
io.ReadFull 関数
io.ReadFull は io パッケージで提供されるヘルパー関数で、指定された io.Reader から、与えられたバイトスライス buf を完全に埋めるまでデータを読み込もうとします。
func ReadFull(r Reader, buf []byte) (n int, err error)
この関数の重要な特性は以下の通りです。
- 正確なバイト数の読み込み:
io.ReadFullはlen(buf)バイトを正確に読み込むことを試みます。 - エラーハンドリング:
- 成功:
len(buf)バイトを完全に読み込めた場合、nはlen(buf)となり、errはnilとなります。 io.EOF:io.ReadFullが呼び出された時点で、まだ1バイトも読み込んでいない状態でストリームの終端に達した場合(つまり、nが0の場合)、io.EOFエラーを返します。io.ErrUnexpectedEOF:io.ReadFullがlen(buf)バイトを読み込む前にストリームの終端に達した場合(つまり、n > 0だがn < len(buf)の場合)、io.ErrUnexpectedEOFエラーを返します。これは、期待したよりも少ないバイト数しか読み込めなかったことを示します。- その他のエラー: 基になる
io.Readerが他のエラーを返した場合、io.ReadFullはそのエラーをそのまま返します。
- 成功:
この特性から、io.ReadFull が nil ではないエラーを返した場合、それは常に len(buf) バイトを読み込めなかったことを意味します。逆に、エラーが nil であれば、len(buf) バイトが完全に読み込まれたことを意味します。
技術的詳細
元のコードでは、io.ReadFull の戻り値 n と err を両方チェックしていました。
n, err := io.ReadFull(rand.Reader, b)
if n != len(b) || err != nil {
fmt.Println("error:", err)
return
}
ここで、io.ReadFull の特性を考慮すると、以下のことが言えます。
- もし
io.ReadFullがlen(b)バイトを完全に読み込めた場合、nはlen(b)となり、errはnilとなります。この場合、n != len(b)はfalse、err != nilもfalseとなり、if文の条件はfalseとなります。 - もし
io.ReadFullがlen(b)バイトを完全に読み込めなかった場合、io.ReadFullは必ず非nilのエラー (io.EOF,io.ErrUnexpectedEOF, または基になるリーダーからの他のエラー) を返します。この場合、err != nilはtrueとなり、if文の条件はtrueとなります。
したがって、n != len(b) という条件は err != nil が true である場合にのみ意味を持つため、err != nil のチェックだけで十分であり、n のチェックは冗長になります。
新しいコードでは、この冗長なチェックを削除し、err のみを確認するように変更されています。
_, err := io.ReadFull(rand.Reader, b)
if err != nil {
fmt.Println("error:", err)
return
}
これにより、コードはより簡潔になり、io.ReadFull の意図する挙動をより明確に反映しています。また、n の戻り値が使用されないため、_ で破棄することで、未使用変数の警告も回避できます。
コアとなるコードの変更箇所
--- a/src/pkg/crypto/rand/example_test.go
+++ b/src/pkg/crypto/rand/example_test.go
@@ -16,8 +16,8 @@ import (
func ExampleRead() {
c := 10
b := make([]byte, c)
- n, err := io.ReadFull(rand.Reader, b)
- if n != len(b) || err != nil {
+ _, err := io.ReadFull(rand.Reader, b)
+ if err != nil {
fmt.Println("error:", err)
return
}
コアとなるコードの解説
変更は src/pkg/crypto/rand/example_test.go ファイルの ExampleRead 関数内で行われています。
-
変更前:
n, err := io.ReadFull(rand.Reader, b) if n != len(b) || err != nil { fmt.Println("error:", err) return }ここでは
io.ReadFullの戻り値n(読み込んだバイト数) とerr(エラー) の両方を受け取っています。そして、if文でnが期待されるバイト数len(b)と異なるか、またはerrがnilでない場合にエラー処理を行っています。 -
変更後:
_, err := io.ReadFull(rand.Reader, b) if err != nil { fmt.Println("error:", err) return }変更後では、
io.ReadFullの戻り値のうちnは_(ブランク識別子) で破棄され、errのみを受け取っています。そして、if文ではerrがnilでないかどうかのチェックのみを行っています。
この変更により、io.ReadFull の「指定されたバイト数を読み込めなかった場合は必ずエラーを返す」という保証を最大限に活用し、コードをより簡潔でGoのイディオムに沿ったものにしています。
関連リンク
- Go CL (Change-list): https://golang.org/cl/12731043
- GitHub Issue: https://github.com/golang/go/issues/6089
参考にした情報源リンク
io.ReadFullのドキュメントと特性に関する情報:- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG3rAsHZQ9oWd9ZGDrfNzZI5T86byjc3bXc77sfXAbagko-n3Q4-h-A4lvt1wv9_UzBhWcGSAUFU7GFmJLEJd3CkMyjP0AiNmOEwRJeZ0IkPWBnwdXHVcaMoEGmMptAfE1aqNo=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHS-JlVkPj1yInXC8UUZDRwc7pLhF0lySzDD2z0iCm7BSiqzMZ3iJkj6MHkEkGqQGP5NeWLpPLWbMtTfagtLwyLQzpp6Bej2DLMoOutiQjwA==