相信熟悉 Golang 的小伙伴不少都知道 `條件編譯` 這個事,最近項目中也可能會用到這個東西。所以特意重新學習下,記錄下學習的過程。這樣用的時候記不住了,還可以直接過來看自己的筆記。 > 文章很多內容來源於參考資料,感謝。 ### 1、條件編譯簡介 #### 1.1、為什麼需要條件編譯 在實際的 ...
1. 引言
io.discard
是Go語言標準庫提供一個結構體類型,其在丟棄不需要的數據場景下非常好用。本文我們將從io.discard
類型的基本定義出發,講述其基本使用和實現原理,接著簡單描述 io.discard
的使用場景,基於此完成對 io.discard
類型的介紹。
2. 介紹
2.1 基本定義
io.discard
是 Go語言提供的一個Writer
,這個Writer
比較特殊,其不會做任何事情。它會將寫入的數據立即丟棄,不會做任何處理。其定義如下:
type discard struct{}
func (discard) Write(p []byte) (int, error) {}
func (discard) WriteString(s string) (int, error) {}
func (discard) ReadFrom(r Reader) (n int64, err error) {}
discard
結構體類型沒有定義任何欄位,同時還提供了Write
,ReadFrom
和WriteString
方法,Write
方法和WriteString
方法分別接收位元組切片和字元串,然後返回寫入的位元組數。
同時還實現了io.ReaderFrom
介面,這個是為了在使用 io.Copy
函數時,將數據從源複製到io.discard
時,避免不必要的操作。
從上面discard
的定義可以看起來,其不是一個公開類型的結構體類型,所以我們並不能創建結構體實例。事實上Go語言提供了一個io.discard
實例的預定義常量,我們直接使用,無需自己創建實例,定義如下:
var Discard Writer = discard{}
2.2 使用說明
下麵通過一個丟棄網路連接中不再需要的數據的例子,來展示io.Discard
的使用,代碼示例如下:
package main
import (
"fmt"
"io"
"net"
"os"
)
func discardData(conn net.Conn, bytesToDiscard int64) error {
_, err := io.CopyN(io.Discard, conn, bytesToDiscard)
return err
}
func main() {
conn, err := net.Dial("tcp", "example.com:80")
if err != nil {
fmt.Println("連接錯誤:", err)
return
}
defer conn.Close()
bytesToDiscard := int64(1024) // 要丟棄的位元組數
err = discardData(conn, bytesToDiscard)
if err != nil {
fmt.Println("丟棄數據錯誤:", err)
return
}
fmt.Println("數據已成功丟棄。")
}
在上面示例中,我們建立了網路連接,然後連接中的前1024個位元組的數據是不需要的。這個時候,我們通過io.CopyN
函數將數據從conn
拷貝到io.Discard
當中,基於io.Discard
丟棄數據的特性,成功將連接的前1024個位元組丟棄掉,而不需要自定義緩衝區之類的操作,簡單高效。
3. 實現原理
io.Discard
的目的是在某些場景下提供一個滿足io.Writer
介面的實例,但用戶對於數據的寫入操作並不關心。它可以被用作一個黑洞般的寫入目標,默默地丟棄所有寫入它的數據。所以io.discard
的實現也相對比較簡單,不對輸入的數據進行任何處理即可,下麵我們來看具體的實現。
首先是io.discard
結構體的定義,沒有定義任何欄位,因為本來也不需要執行任何寫入操作:
type discard struct{}
而對於Write
和 WriteString
方法,其直接返回了傳入參數的長度,往該Writer
寫入的數據不會被寫入到其他地方,而是被直接丟棄:
func (discard) Write(p []byte) (int, error) {
return len(p), nil
}
func (discard) WriteString(s string) (int, error) {
return len(s), nil
}
同時discard
也實現了io.ReaderFrom
介面,實現了ReadFrom
方法,實現也是非常簡單,從blackHolePool
緩衝池中獲取位元組切片,然後不斷讀取數據,讀取完成之後,再將位元組切片重新放入緩衝池當中:
// 存在一個位元組切片緩衝池
var blackHolePool = sync.Pool{
New: func() any {
b := make([]byte, 8192)
return &b
},
}
func (discard) ReadFrom(r Reader) (n int64, err error) {
// 從緩衝池中取出一個 位元組切片
bufp := blackHolePool.Get().(*[]byte)
readSize := 0
for {
// 不斷讀取數據,bufp 只是作為一個讀取數據的中介,讀取到的數據並無意義
readSize, err = r.Read(*bufp)
n += int64(readSize)
if err != nil {
// 將位元組切片 重新放入到 blackHolePool 當中
blackHolePool.Put(bufp)
if err == EOF {
return n, nil
}
return
}
}
}
在io.Copy
函數中,將調用discard
中的ReadFrom
方法,能夠將Writer
中的所有數據讀取完,然後丟棄掉。
4. 使用場景
io.Discard
給我們提供了一個io.Writer
介面的實例,同時其又不會真實得寫入數據,這個在某些場景下非常有用。
有時候,我們可能需要一個實現io.Writer
介面的實例,但是我們並不關心數據寫入Writer
的結果,也不關心數據是否寫到了哪個地方,此時io.Discard
就給我們提供了一個方便的解決方案。同時io.Discard
可以作為一個黑洞寫入目標,能夠將數據默默丟棄掉,不會進行實際的處理和存儲。
所以如果我們想要丟棄某些數據,亦或者是需要一個io.Writer
介面的實例,但是對於寫入結果不需要關註時,此時使用io.Discard
是非常合適的。
5. 總結
io.discard
函數是Go語言標準庫中一個實現了Writer
介面的結構體類型,能夠悄無聲息得實現數據的丟棄。 我們先從io.discard
類型的基本定義出發,之後通過一個簡單的示例,展示如何使用io.discard
類型實現對不需要數據的丟棄。
接著我們講述了io.discard
類型的實現原理,其實就是不對寫入的數據執行任何操作。在使用場景下,我們想要丟棄某些數據,亦或者是需要一個io.Writer
介面的實例,但是對於寫入結果不需要關註時,此時使用io.Discard
是非常合適的。
基於此,便完成了對io.discard
類型的介紹,希望對你有所幫助。