fmt.Errorf în Go

Afișare și formatare (fmt) Go 1.0 (%w din Go 1.13)

Creează o eroare cu mesaj formatat; cu %w împachetează o eroare existentă, care poate fi găsită apoi cu errors.Is.

Sintaxă
func Errorf(format string, a ...any) error

Ce face fmt.Errorf

fmt.Errorf funcționează ca Sprintf, dar returnează o valoare de tip error în loc de text. Este modul obișnuit de a crea erori cu detalii: fmt.Errorf("nota %d nu este validă", n).

Verbul special %w (de la wrap, împachetare) pune în mesaj textul unei erori existente și o păstrează înăuntru. Așa adaugi context („la citirea fișierului note.txt: …”) fără să pierzi eroarea originală: errors.Is și errors.As o găsesc în continuare, iar errors.Unwrap o scoate. Din Go 1.20 poți folosi mai multe %w în același apel.

Cu %v în loc de %w se copiază doar textul erorii; legătura cu eroarea originală se pierde.

Parametri

Parametru Descriere
format Mesajul erorii, cu verbe ca la Printf. Convenția Go: litere mici la început, fără punct la final.
a ...any Valorile pentru verbe; pentru %w valoarea trebuie să fie de tip error.

Ce returnează

O valoare error. Cu %w, eroarea are metoda Unwrap care dă eroarea împachetată.

Exemple

Exemplul 1: Eroare cu detalii

package main

import "fmt"

func verificaNota(n int) error {
	if n < 1 || n > 10 {
		return fmt.Errorf("nota %d nu este între 1 și 10", n)
	}
	return nil
}

func main() {
	for _, n := range []int{9, 12, 0} {
		if err := verificaNota(n); err != nil {
			fmt.Println("Eroare:", err)
		} else {
			fmt.Println(n, "este o notă validă")
		}
	}
}

Rezultat

9 este o notă validă
Eroare: nota 12 nu este între 1 și 10
Eroare: nota 0 nu este între 1 și 10

Exemplul 2: Împachetare cu %w și verificare cu errors.Is

Mesajul final are tot contextul, dar errors.Is recunoaște eroarea originală ErrStocEpuizat. Cu %v legătura s-ar pierde.

package main

import (
	"errors"
	"fmt"
)

var ErrStocEpuizat = errors.New("stoc epuizat")

func rezerva(produs string, bucati int) error {
	if bucati > 3 {
		return fmt.Errorf("rezervare %s (%d buc.): %w", produs, bucati, ErrStocEpuizat)
	}
	return nil
}

func main() {
	err := rezerva("cafea", 5)
	fmt.Println(err)
	fmt.Println("este ErrStocEpuizat?", errors.Is(err, ErrStocEpuizat))
	fmt.Println("eroarea din interior:", errors.Unwrap(err))

	copie := fmt.Errorf("rezervare: %v", ErrStocEpuizat)
	fmt.Println("cu verbul v:", errors.Is(copie, ErrStocEpuizat))
}

Rezultat

rezervare cafea (5 buc.): stoc epuizat
este ErrStocEpuizat? true
eroarea din interior: stoc epuizat
cu verbul v: false

Exemplul 3: Context în jurul unei erori din biblioteca standard

errors.As scoate eroarea concretă *strconv.NumError din interior, chiar dacă a fost împachetată.

package main

import (
	"errors"
	"fmt"
	"strconv"
)

func citesteVarsta(text string) (int, error) {
	v, err := strconv.Atoi(text)
	if err != nil {
		return 0, fmt.Errorf("vârsta %q: %w", text, err)
	}
	return v, nil
}

func main() {
	_, err := citesteVarsta("douăzeci")
	fmt.Println(err)
	var numErr *strconv.NumError
	if errors.As(err, &numErr) {
		fmt.Println("funcția:", numErr.Func, "| cauza:", numErr.Err)
	}
}

Rezultat

vârsta "douăzeci": strconv.Atoi: parsing "douăzeci": invalid syntax
funcția: Atoi | cauza: invalid syntax

Greșeli frecvente

  • Folosești %v când vrei să păstrezi eroarea. errors.Is va returna false; pentru împachetare folosește %w.
  • Pui %w cu o valoare care nu este error. go vet semnalează greșeala, iar la rulare apare %!w(...) în mesaj.
  • Scrii mesaje cu majusculă și punct („Nota invalidă.”). Erorile sunt de obicei puse una în alta („citire: conversie: nota invalidă”), deci convenția este literă mică și fără punct.

Comenzi înrudite

  • fmt.Printf: Afișează text formatat cu %d, %s, %.2f etc.; Sprintf returnează textul, Fprintf îl scrie într-un fișier sau alt Writer.
  • strconv.Atoi: Atoi transformă un text în număr întreg (int) și returnează eroare dacă nu se poate; Itoa face invers.