sync.OnceValue în Go

Returnează o funcție care calculează valoarea o singură dată și o dă înapoi la fiecare apel; OnceValues pentru două valori.

Sintaxă
func OnceValue[T any](f func() T) func() T
func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2)

Ce face sync.OnceValue

sync.OnceValue(f) primește o funcție care calculează ceva și returnează o funcție nouă. La primul apel rulează f și ține minte rezultatul; la apelurile următoare returnează direct rezultatul păstrat. Este cel mai simplu mod de a face o valoare globală „leneșă”, calculată abia când e nevoie de ea.

sync.OnceValues(f) face același lucru pentru funcții cu două rezultate, cel mai des (valoare, error): și eroarea este păstrată și returnată la fiecare apel. Ambele sunt generice: tipul rezultatului este dedus din f.

Parametri

Parametru Descriere
f Funcția care calculează valoarea (sau perechea de valori).

Ce returnează

O funcție care returnează mereu același rezultat (sau aceeași pereche).

Exemple

Exemplul 1: Tabel calculat o singură dată

package main

import (
	"fmt"
	"sync"
)

var numerePrime = sync.OnceValue(func() []int {
	fmt.Println("(calculez numerele prime până la 50)")
	var prime []int
	for n := 2; n <= 50; n++ {
		prim := true
		for _, p := range prime {
			if n%p == 0 {
				prim = false
				break
			}
		}
		if prim {
			prime = append(prime, n)
		}
	}
	return prime
})

func main() {
	fmt.Println("Primele 5:", numerePrime()[:5])
	fmt.Println("Câte sunt:", len(numerePrime()))
}

Rezultat

(calculez numerele prime până la 50)
Primele 5: [2 3 5 7 11]
Câte sunt: 15

Exemplul 2: OnceValues cu valoare și eroare

Citirea portului se face o singură dată; și eroarea este păstrată.

package main

import (
	"fmt"
	"strconv"
	"sync"
)

func citestePort(text string) func() (int, error) {
	return sync.OnceValues(func() (int, error) {
		fmt.Println("(citesc portul din configurare)")
		return strconv.Atoi(text)
	})
}

func main() {
	port := citestePort("8080")
	for range 2 {
		p, err := port()
		fmt.Println("port:", p, "eroare:", err)
	}

	gresit := citestePort("opt mii")
	for range 2 {
		_, err := gresit()
		fmt.Println("eroare:", err)
	}
}

Rezultat

(citesc portul din configurare)
port: 8080 eroare: <nil>
port: 8080 eroare: <nil>
(citesc portul din configurare)
eroare: strconv.Atoi: parsing "opt mii": invalid syntax
eroare: strconv.Atoi: parsing "opt mii": invalid syntax

Greșeli frecvente

  • Modifici slice-ul sau map-ul returnat de OnceValue. Toți apelanții primesc același obiect, deci modificarea se vede peste tot; copiază-l dacă trebuie schimbat.

Comenzi înrudite

  • sync.Once: Garantează că o funcție rulează o singură dată, chiar dacă Do este apelat de multe goroutine (inițializare leneșă).
  • sync.OnceFunc: Returnează o funcție care rulează f doar la primul apel; apelurile următoare nu fac nimic.