islishude / blog

my web notes
https://islishude.github.io/blog/
101 stars 15 forks source link

Go linter: io.ReadFull 不需要判断返回值长度 #235

Closed islishude closed 4 years ago

islishude commented 4 years ago

ReadFull 返回从Reader读取的字节数量和错误信息

ReadFull(r Reader, buf []byte) (n int, err error)

ReadFull 经常出现在其它辅助函数中,比如随机数生成函数 rand.Read

package rand

import "io"

var Reader io.Reader

func Read(b []byte) (n int, err error) {
    return io.ReadFull(Reader, b)
}

除了判断 err 是否不为空之外,经常有代码还会判断这个 n 的长度是否和 b 的长度一样,其实这是没必要的,io.ReadFull 内部保证了如果 err == nil 那么 n == len(b)。

如实现代码所示,如果 Reader 内字节长度小于所需长度那么一定会返回 EOF 错误或者 ErrUnexpectedEOF 错误。

func ReadFull(r Reader, buf []byte) (n int, err error) {
    return ReadAtLeast(r, buf, len(buf))
}

func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
    if len(buf) < min {
        return 0, ErrShortBuffer
    }
    for n < min && err == nil {
        var nn int
        nn, err = r.Read(buf[n:])
        n += nn
    }
    if n >= min {
        err = nil
    } else if n > 0 && err == EOF {
        err = ErrUnexpectedEOF
    }
    return
}

使用下面实际代码运行来看,确实不需要判断返回长度

package main

import (
    "bytes"
    "fmt"
    "io"
)

func main() {
    // 无数据 0 EOF
    {
        src := bytes.NewReader(make([]byte, 0))
        dsc := make([]byte, 40)
        fmt.Println(io.ReadFull(src, dsc))
    }

    // 数据不够 32 unexpected EOF
    {
        src := bytes.NewReader(make([]byte, 32))
        dsc := make([]byte, 40)
        fmt.Println(io.ReadFull(src, dsc))
    }

    // 数据正好 32 <nil>
    {
        src := bytes.NewReader(make([]byte, 32))
        dsc := make([]byte, 32)
        fmt.Println(io.ReadFull(src, dsc))
    }

    // 数据更多 32 <nil>
    {
        src := bytes.NewReader(make([]byte, 40))
        dsc := make([]byte, 32)
        fmt.Println(io.ReadFull(src, dsc))
    }
}