在go語言中,eof是指檔案結尾錯誤,是Go語言中最重要的錯誤變量,存在於io套件中,用來表示輸入流的結尾。因為每個檔案都有一個結尾,所以「io.EOF」很多時候並不能算是一個錯誤,它更重要的是表示一個輸入流結束了。
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
golang 檔案結尾錯誤(EOF)
函數經常會傳回多種錯誤,這對終端使用者來說可能會很有趣,但對程序而言,這使得情況變得複雜。很多時候,程式必須根據錯誤類型,做出不同的回應。讓我們考慮這樣一個例子:
從檔案讀取n個位元組。如果n等於檔案的長度,讀取過程的任何錯誤都表示失敗。如果n小於檔案的長度,呼叫者會重複的讀取固定大小的資料直到檔案結束。這會導致呼叫者必須分別處理由檔案結束引起的各種錯誤。
基於這樣的原因,io套件
保證任何由檔案結束引起的讀取失敗都會回傳同一個錯誤-io.EOF,該錯誤在io套件中定義:
package io import "errors" // EOF is the error returned by Read when no more input is available. var EOF = errors.New("EOF")
認識io.EOF
io.EOF是io套件中的變數, 表示檔案結束的錯誤:
package io23var EOF = errors.New("EOF")
#也透過以下指令查看詳細文件:
$ go doc io.EOF var EOF = errors.New("EOF") EOF is the error returned by Read when no more input is available. Functions should return EOF only to signal a graceful end of input. If the EOF occurs unexpectedly in a structured data stream, the appropriate error is either ErrUnexpectedEOF or some other error giving more detail. $
io.EOF大約可以算是Go語言中最重要的錯誤變數了, 它用來表示輸入流的結尾. 因為每個檔案都有一個結尾, 所以io.EOF很多時候並不能算是一個錯誤, 它更重要的是表示一個輸入流結束了。
io.EOF設計的缺陷
#可惜標準庫中的io.EOF的設計是有問題的. 首先EOF是End- Of-File的縮寫, 根據Go語言的習慣大寫字母縮寫一般表示常數. 可惜io.EOF被錯誤地定義成了變數, 這導致了API權限的擴散. 而最小化API權限是任何一個模組或函數設計的最高要求. 透過最小化的權限, 可以儘早發現程式碼中不必要的錯誤.
例如Go語言一個重要的安全設計就是禁止隱式的類型轉換. 因此這個設計我們就可以很容易發現程式的BUG. 另外Go語言禁止定義沒有被使用到的局部變數(函數參數除外, 因此函數參數是函數介面的一個部分)和禁止導入沒有用到的包都是最小化權限的最佳實務.這些最小API權限的設計不僅改進了程式的品質, 也提高了編譯工具的性能和輸出的目標檔案.
因為EOF被定義成一個變數, 這導致了該變數可能會被惡意改變. 下面的程式碼就是一種優雅的埋坑方式:
func init() {2 io.EOF = nil3}
這雖然是一個段子, 但是卻真實地暴漏了EOF接口的設計缺陷: 它存在嚴重的安全隱患. 變量的類型似乎也在暗示使用者可以放心地修改變數的值. 因此說EOF是一個不安全也不優雅的設計.
io.EOF改為常數
一個顯然的改進思路是將io.EOF定義為常數. 但是因為EOF對應一個表示error接口類型, 而Go語言目前的常量語法並不支持定義常量類型的接口. 但是我們可以通過一些技巧繞過這個限制.
Go語言的常數有bool/int/float/string/nil這幾種主要類型. 常數不僅僅不包含接口等複雜類型, 甚至連常量的數組或結構體都不支援! 不過常數有一個重要的擴充規則: 以bool/int/float/string/nil為基礎型別定義的新型別也支援常數.
例如, 我們重新定義一個字串型別, 它也可以支援常數的:
type MyString string2const name MyString = "chai2010"
這個例子中MyString是一個新定義的類型, 可以定義這種類型的常數, 因為它的底層的string類型是支援常數的.
那麼io.EOF的底層型別是什麼呢? EOF是透過errors.New("EOF")定義的, 下面是這個函數的實現:
package errors // New returns an error that formats as the given text. func New(text string) error { return &errorString{text} } // errorString is a trivial implementation of error. type errorString struct { s string } func (e *errorString) Error() string { return e.s }
因此io.EOF底層的型別是errors .errorString結構體. 而結構體類型是不支援定義常數的. 不過errors.errorString結構體中只有一個字串型別, io.EOF對應的錯誤字串正是"EOF".
我們可以為EOF重新實作一個以字串為底層類型的新錯誤類型:
package io type errorString string func (e errorString) Error() string { return string(e) }
這個新的io.errorString實作了兩個特性: 首先是滿足了error介面; 其次它是基於string類型重新定義, 因此支援定義常數. 因此我們可以基於errorString重新將io.EOF定義為常數:
const EOF = errorString("EOF")
這樣EOF就變成了編譯時可以確定的常數類型, 常數的值依然是“EOF”字串. 但是也帶來了新的問題: EOF已經不再是一個介面類型, 它會破壞舊程式碼的兼容性嗎?
以上是go語言eof錯誤是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!