io.Pipe în Go

Creează un pipe în memorie: ce scrie o goroutine la capătul Writer citește alta la capătul Reader.

Sintaxă
func Pipe() (*PipeReader, *PipeWriter)

Ce face io.Pipe

io.Pipe leagă un cod care vrea un io.Writer de un cod care vrea un io.Reader, fără buffer intermediar: fiecare Write așteaptă până când datele sunt citite. De aceea scriitorul și cititorul trebuie să fie în goroutine diferite.

Scriitorul semnalează sfârșitul cu pw.Close() (cititorul primește io.EOF) sau o eroare cu pw.CloseWithError(err). Exemplu tipic: generezi JSON cu json.NewEncoder(pw) și îl trimiți direct ca corp al unei cereri HTTP, fără să-l construiești întâi în memorie.

Spre deosebire de os.Pipe, io.Pipe există doar în program și nu poate fi dat unui proces copil.

Ce returnează

Capătul de citire (*io.PipeReader) și capătul de scriere (*io.PipeWriter).

Exemple

Exemplul 1: Producător și consumator

package main

import (
    "bufio"
    "fmt"
    "io"
)

func main() {
    pr, pw := io.Pipe()

    go func() {
        defer pw.Close()
        for i := 1; i <= 3; i++ {
            fmt.Fprintf(pw, "comanda %d\n", 1040+i)
        }
    }()

    s := bufio.NewScanner(pr)
    for s.Scan() {
        fmt.Println("procesez:", s.Text())
    }
}

Rezultat

procesez: comanda 1041
procesez: comanda 1042
procesez: comanda 1043

Exemplul 2: JSON generat din mers

Encoder-ul scrie în pipe, iar celălalt capăt e citit ca un io.Reader (aici cu io.ReadAll, în practică de http.Post).

package main

import (
    "encoding/json"
    "fmt"
    "io"
)

type Produs struct {
    Nume string  `json:"nume"`
    Pret float64 `json:"pret"`
}

func main() {
    pr, pw := io.Pipe()
    go func() {
        err := json.NewEncoder(pw).Encode([]Produs{{"carte", 45.5}, {"pix", 3}})
        pw.CloseWithError(err)
    }()
    corp, err := io.ReadAll(pr)
    fmt.Print(string(corp))
    fmt.Println("eroare:", err)
}

Rezultat

[{"nume":"carte","pret":45.5},{"nume":"pix","pret":3}]
eroare: <nil>

Exemplul 3: Eroare transmisă cititorului

package main

import (
    "errors"
    "fmt"
    "io"
)

func main() {
    pr, pw := io.Pipe()
    go func() {
        pw.Write([]byte("date parțiale..."))
        pw.CloseWithError(errors.New("conexiunea cu baza de date s-a pierdut"))
    }()
    date, err := io.ReadAll(pr)
    fmt.Printf("%q\n", date)
    fmt.Println("eroare:", err)
}

Rezultat

"date parțiale..."
eroare: conexiunea cu baza de date s-a pierdut

Greșeli frecvente

  • Scrii și citești din pipe în aceeași goroutine. Write așteaptă un cititor care nu vine niciodată: programul se blochează („all goroutines are asleep – deadlock”).
  • Uiți pw.Close(). Cititorul așteaptă la nesfârșit după ultimele date.

Comenzi înrudite

  • os.Pipe: Creează un pipe al sistemului de operare: ce scrii la un capăt (w) poți citi la celălalt (r).
  • io.Reader: Interfața pentru orice sursă de date din care se poate citi: fișiere, text, conexiuni, răspunsuri HTTP.
  • io.Writer: Interfața pentru orice destinație în care se pot scrie octeți: fișiere, ecran, buffer-e, răspunsuri HTTP.
  • io.Copy: Copiază toate datele dintr-un Reader într-un Writer, pe bucăți, fără să le țină pe toate în memorie.