ToB企服应用市场:ToB评测及商务社交产业平台

标题: 一文了解io包中的discard类型 [打印本页]

作者: 宁睿    时间: 2023-7-13 06:08
标题: 一文了解io包中的discard类型
1. 引言

io.discard是Go语言标准库提供一个结构体类型,其在丢弃不需要的数据场景下非常好用。本文我们将从io.discard 类型的基本定义出发,讲述其基本使用和实现原理,接着简单描述 io.discard 的使用场景,基于此完成对 io.discard 类型的介绍。
2. 介绍

2.1 基本定义

io.discard 是 Go语言提供的一个Writer,这个Writer 比较特殊,其不会做任何事情。它会将写入的数据立即丢弃,不会做任何处理。其定义如下:
  1. type discard struct{}
  2. func (discard) Write(p []byte) (int, error) {}
  3. func (discard) WriteString(s string) (int, error) {}
  4. 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 实例的预定义常量,我们直接使用,无需自己创建实例,定义如下:
  1. var Discard Writer = discard{}
复制代码
2.2 使用说明

下面通过一个丢弃网络连接中不再需要的数据的例子,来展示io.Discard 的使用,代码示例如下:
  1. package main
  2. import (
  3.         "fmt"
  4.         "io"
  5.         "net"
  6.         "os"
  7. )
  8. func discardData(conn net.Conn, bytesToDiscard int64) error {
  9.         _, err := io.CopyN(io.Discard, conn, bytesToDiscard)
  10.         return err
  11. }
  12. func main() {
  13.         conn, err := net.Dial("tcp", "example.com:80")
  14.         if err != nil {
  15.                 fmt.Println("连接错误:", err)
  16.                 return
  17.         }
  18.         defer conn.Close()
  19.         bytesToDiscard := int64(1024) // 要丢弃的字节数
  20.         err = discardData(conn, bytesToDiscard)
  21.         if err != nil {
  22.                 fmt.Println("丢弃数据错误:", err)
  23.                 return
  24.         }
  25.         fmt.Println("数据已成功丢弃。")
  26. }
复制代码
在上面示例中,我们建立了网络连接,然后连接中的前1024个字节的数据是不需要的。这个时候,我们通过io.CopyN 函数将数据从conn 拷贝到io.Discard 当中,基于io.Discard 丢弃数据的特性,成功将连接的前1024个字节丢弃掉,而不需要自定义缓冲区之类的操作,简单高效。
3. 实现原理

io.Discard的目的是在某些场景下提供一个满足io.Writer接口的实例,但用户对于数据的写入操作并不关心。它可以被用作一个黑洞般的写入目标,默默地丢弃所有写入它的数据。所以io.discard 的实现也相对比较简单,不对输入的数据进行任何处理即可,下面我们来看具体的实现。
首先是io.discard 结构体的定义,没有定义任何字段,因为本来也不需要执行任何写入操作:
  1. type discard struct{}
复制代码
而对于Write 和 WriteString 方法,其直接返回了传入参数的长度,往该Writer 写入的数据不会被写入到其他地方,而是被直接丢弃:
  1. func (discard) Write(p []byte) (int, error) {
  2.    return len(p), nil
  3. }
  4. func (discard) WriteString(s string) (int, error) {
  5.    return len(s), nil
  6. }
复制代码
同时discard 也实现了io.ReaderFrom 接口,实现了ReadFrom 方法,实现也是非常简单,从blackHolePool 缓冲池中获取字节切片,然后不断读取数据,读取完成之后,再将字节切片重新放入缓冲池当中:
  1. // 存在一个字节切片缓冲池
  2. var blackHolePool = sync.Pool{
  3.    New: func() any {
  4.       b := make([]byte, 8192)
  5.       return &b
  6.    },
  7. }
  8. func (discard) ReadFrom(r Reader) (n int64, err error) {
  9.    // 从缓冲池中取出一个 字节切片
  10.    bufp := blackHolePool.Get().(*[]byte)
  11.    readSize := 0
  12.    for {
  13.       // 不断读取数据,bufp 只是作为一个读取数据的中介,读取到的数据并无意义
  14.       readSize, err = r.Read(*bufp)
  15.       n += int64(readSize)
  16.       if err != nil {
  17.          // 将字节切片 重新放入到 blackHolePool 当中
  18.          blackHolePool.Put(bufp)
  19.          if err == EOF {
  20.             return n, nil
  21.          }
  22.          return
  23.       }
  24.    }
  25. }
复制代码
在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 类型的介绍,希望对你有所帮助。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4