• English
  • Example: io.ReadFull

    io.ReadFull reads from an io.Reader until the buffer is completely filled or an error occurs.

    Unlike a standard io.Read, io.ReadFull will not return early with a success status if it reads fewer bytes than requested. It continues reading until one of the following conditions is met:

    • Success: The buffer (len(buf)) is completely filled.
    • Failure: An error occurs before the buffer is completely filled. If the stream ends early, the returned error is typically io.ErrUnexpectedEOF.

    Common Use Cases

    • Binary protocol parsing (e.g., reading fixed-length headers)
      • Framed protocol parsing (reconstructing fixed-length protocol headers over TCP streams)
    • Reading file headers or metadata
    • Processing fixed-size messages

    Quick Start

    The simplest scenario: precisely reading 5 bytes from a data source.

    package main
    
    import (
        "fmt"
        "io"
        "strings"
    )
    
    func main() {
        // 1. Create a Reader to simulate a data source
        src := strings.NewReader("Hello, io.ReadFull from gobase.net.")
    
        // 2. Read a fixed length of data
        buf := make([]byte, 5)
        _, err := io.ReadFull(src, buf)
        if err != nil {
            fmt.Printf("failed to read: %v\n", err)
            return
        }
    
        // 3. 'buf' now contains the fully read data
        fmt.Printf("%s\n",buf)
    }
    

    Output:

    Hello
    

    The Core Value A single call to reader.Read(buf) does not guarantee that buf will be completely filled. It may return after reading only part of the requested data, leaving the caller responsible for repeatedly calling Read until enough bytes have been collected.

    In contrast, io.ReadFull(reader, buf) establishes a strict API contract: as long as it returns err == nil, it guarantees that exactly len(buf) bytes have been read.


    Parsing Fixed-Length Protocol Headers

    In binary protocols, a fixed-size header is typically defined at the very beginning of a message.

    For example:

    +-----------------------+-----------------------+
    |  Magic Code (4 bytes) |  Body Length (4 bytes)|
    +-----------------------+-----------------------+
    |                    Payload                    |
    +-----------------------+-----------------------+
    

    When parsing this protocol, you must fully read the 8-byte header first; otherwise, you cannot determine the length of the trailing payload.

    package main
    
    import (
        "encoding/binary"
        "fmt"
        "io"
        "strings"
    )
    
    func main() {
        // Simulate a network stream:
        // [Magic Code (4 bytes)] [Payload Length (4 bytes)] [Payload]
        data := "\x00\x00\x00\x00\x00\x00\x00\x05hello"
        reader := strings.NewReader(data)
        
        // The header length is 8 bytes
        headerMagic := make([]byte, 4)
        headerLength := make([]byte, 4)
        
        // Read the Magic Code and Length fields completely.
         _, err := io.ReadFull(reader, headerMagic)
         if err != nil {
            fmt.Printf("failed to read header: %v\n", err)
            return
        }
        _, err = io.ReadFull(reader, headerLength)
        if err != nil {
            fmt.Printf("failed to read header: %v\n", err)
            return
        }
        
        // Parse the length
        length := binary.BigEndian.Uint32(headerLength)
        fmt.Printf("Expected payload length: %d\n", length)
    }
    

    Output:

    Expected payload length: 5
    

    Edge Case Handling: Error Mechanism on Insufficient Data

    io.ReadFull eventually returns an error after exhausting the available data.

    package main
    
    import (
        "fmt"
        "io"
        "strings"
    )
    
    func main() {
        src := strings.NewReader("abc") // Only 3 bytes
        buf := make([]byte, 5)          // Expecting 5 bytes
    
        _, err := io.ReadFull(src, buf)
        fmt.Printf("%v\n", err)
    }
    

    Output:

    unexpected EOF
    

    This error indicates that:

    • The Reader reached its end.
    • However, the requested data length was not fully met.

    This is distinct from a standard io.EOF:

    ErrorMeaning
    nilThe buffer was completely filled.
    io.EOFNo bytes were read because the stream was already at EOF.
    io.ErrUnexpectedEOFThe stream ended after some bytes were read but before the buffer was filled.
    Tip

    TCP is a byte stream rather than a message-oriented protocol. A single Write by the sender does not necessarily correspond to a single Read by the receiver. io.ReadFull is commonly used to reconstruct fixed-length portions of a protocol, such as headers, before parsing the remaining payload.