Go語言Writer和Reader介面簡述

2020-07-16 10:04:50
Go語言中 io 包是圍繞著實現了 io.Writer 和 io.Reader 介面型別的值而構建的。由於 io.Writer 和 io.Reader 提供了足夠的抽象,這些 io 包裡的函數和方法並不知道資料的型別,也不知道這些資料在物理上是如何讀和寫的。讓我們先來看一下 io.Writer 介面的宣告,程式碼如下所示。
type Writer interface {
    Write(p []byte) (n int, err error)
}
上述程式碼中展示了 io.Writer 介面的宣告。這個介面宣告了唯一一個方法 Write,這個方法接受一個 byte 切片,並返回兩個值。第一個值是寫入的位元組數,第二個值是 error 錯誤值。

Write 從 p 裡向底層的資料流寫入 len(p) 位元組的資料。這個方法返回從 p 裡寫出的位元組數(0 <= n <= len(p)),以及任何可能導致寫入提前結束的錯誤。Write 在返回 n < len(p) 的時候,必須返回某個非 nil 值的 error。Write 絕不能改寫切片裡的資料,哪怕是臨時修改也不行。

上述的規則來自標準庫。這些規則意味著 Write 方法的實現需要試圖寫入被傳入的 byte 切片裡的所有資料。但是,如果無法全部寫入,那麼該方法就一定會返回一個錯誤。

返回的寫入位元組數可能會小於 byte 切片的長度,但不會出現大於的情況。最後,不管什麼情況,都不能修改 byte 切片裡的資料。

io.Reader 介面的宣告如下所示。
type Reader interface {
    Read(p []byte) (n int, err error)
}
上述程式碼中的 io.Reader 介面宣告了一個方法 Read,這個方法接受一個 byte 切片,並返回兩個值。第一個值是讀入的位元組數,第二個值是 error 錯誤值。實現 Read 方法需要注意以下幾點:

1) Read 最多讀入 len(p) 位元組,儲存到 p。這個方法返回讀入的位元組數(0 <= n <= len(p))和任何讀取時發生的錯誤。即便 Read 返回的 n < len(p),方法也可能使用所有 p 的空間儲存臨時資料。如果資料可以讀取,但是位元組長度不足 len(p),習慣上 Read 會立刻返回可用的資料,而不等待更多的資料。

2) 當成功讀取 n > 0 位元組後,如果遇到錯誤或者檔案讀取完成,Read 方法會返回讀入的位元組數。方法可能會在本次呼叫返回一個非 nil 的錯誤,或者在下一次呼叫時返回錯誤(同時 n == 0)。這種情況的的一個例子是,在輸入的流結束時,Read 會返回非零的讀取位元組數,可能會返回 err == EOF,也可能會返回 err == nil。無論如何,下一次呼叫 Read 應該返回 0, EOF。

3) 呼叫者在返回的 n > 0 時,總應該先處理讀入的資料,再處理錯誤 err。這樣才能正確操作讀取一部分位元組後發生的 I/O 錯誤。EOF 也要這樣處理。

4) Read 的實現不鼓勵返回 0 個讀取位元組的同時,返回 nil 值的錯誤。呼叫者需要將這種返回狀態視為沒有做任何操作,而不是遇到讀取結束。

標準庫裡列出了實現 Read 方法的 4 條規則。

第一條規則表明,該實現需要試圖讀取資料來填滿被傳入的 byte 切片。允許出現讀取的位元組數小於 byte 切片的長度,並且如果在讀取時已經讀到資料但是資料不足以填滿 byte 切片時,不應該等待新資料,而是要直接返回已讀資料。

第二條規則提供了應該如何處理達到檔案末尾(EOF)的情況的指導。當讀到最後一個位元組時,可以有兩種選擇。一種是 Read 返回最終讀到的位元組數,並且返回 EOF 作為錯誤值,另一種是返回最終讀到的位元組數,並返回 nil 作為錯誤值。在後一種情況下,下一次讀取的時候,由於沒有更多的資料可供讀取,需要返回 0 作為讀到的位元組數,以及 EOF 作為錯誤值。

第三條規則是給呼叫 Read 的人的建議。任何時候 Read 返回了讀取的位元組數,都應該優先處理這些讀取到的位元組,再去檢查 EOF 錯誤值或者其他錯誤值。

第四條規則是約束建議 Read 方法的實現永遠不要返回 0 個讀取位元組的同時返回 nil 作為錯誤值。如果沒有讀到值,Read 應該總是返回一個錯誤。