---
id: 1a31e06c0d969b4b6cbbc590259cf089
author: iwanhae
title: Go 1.27, alebo Ako som sa naučil prestať sa obávať a milovať generické metódy
description: Generické metódy, encoding/json/v2, štandardný balík uuid a detektor únikov goroutín, ktorý naozaj funguje. Prehliadka Go 1.27 s príkladmi, ktoré boli všetky skutočne spustené.
language: sk
date: 2026-08-15T02:08:43.129534158Z
path: /blog/posts/go-1-27-or-how-i-learned-to-stop-worrying-and-love-generic-methods-za800e454
category: release
---

![Prehľad Go 1.27](/assets/images/whats-new-in-go1.27/overview.webp)

Go 1.27 prichádza v auguste 2026 a tentoraz sa zmenil samotný jazyk. Nie zmena typu „pridali sme novú funkciu do `slices`“, ale skutočná zmena. Generické metódy sú zavedené. Jedenásťročná otvorená otázka bola uzavretá. `encoding/json` bol potichu nahradený novým enginom, kým lietadlo ešte letelo.

Každý príklad nižšie bol spustený na `go1.27rc3` (darwin/arm64). Každý výstupný blok je skutočný výstup, skopírovaný vrátane chybových hlásení. Ak sa niečo javí zvláštne, je to preto, lebo kompilátor skutočne vypísal práve toto.

```bash
go install golang.org/dl/go1.27rc3@latest
go1.27rc3 download
```

## 1. Jazyk sa zmenil (áno, naozaj)

### 1.1. Generické metódy

Od Go 1.18 máme generiky a od Go 1.18 máme aj Rozhovor. Priebeh je tento:

> „Len si napíšem metódu `Map` pre môj typ slice…“
>
> `method must have no type parameters`
>
> „…dobre. Takže funkcia na úrovni balíka.“

Metódy mohli používať iba typové parametre deklarované v _receiveri_. Vlastná metóda nemohla zaviesť nové. Každá generická transformácia sa preto musela presunúť do rozsahu balíka, kde ležala vedľa ďalších sedemnástich voľne plávajúcich pomocných funkcií s názvami typu `MapSlice`.

Go 1.27 to opravuje:

```go
type List[E any] []E

// F je typový parameter deklarovaný samotnou metódou
func (l List[E]) Apply[F any](f func(E) F) List[F] {
	r := make(List[F], len(l))
	for i, x := range l {
		r[i] = f(x)
	}
	return r
}

func main() {
	l := List[int]{1, 2, 3}
	fmt.Println(l.Apply(func(i int) string { return fmt.Sprint(i * 10) }))
	// [10 20 30]
}
```

Receiver ani nemusí byť generický. Obyčajná štruktúra môže mať generickú metódu:

```go
type Bag struct{ items []any }

func (b *Bag) Add[T any](v T) { b.items = append(b.items, v) }
```

Predtým, než dnes popoludní prepíšete celú kódovú základňu, existujú **dve obmedzenia** a sú dôležitejšie, než vyzerajú:

1. Metódy rozhrania nemôžu deklarovať typové parametre.
2. Generické metódy nemôžu implementovať metódy rozhrania.

Práve to druhé vás bude páliť:

```go
type Adder interface{ Add(int) int }

type G struct{}

func (G) Add[T any](v T) T { return v }

var _ Adder = G{}
```

```
cannot use G{} (value of struct type G) as Adder value in variable declaration:
	G does not implement Adder (wrong type for method Add)
		have Add[T any](T) T
		want Add(int) int
```

Generické metódy a dynamické vyhľadávanie metód sa teda nedajú kombinovať. Nejde o lakomosť tímu Go: generická metóda má nekonečne veľa inštanciácií, kým tabuľka metód rozhrania musí byť konečná a známa v čase kompilácie. Nekonečnú vec nemožno uložiť do konečnej tabuľky. Vesmír povedal nie.

Praktické čítanie: generické metódy slúžia pre konkrétne typy s API v štýle pomocných funkcií. Všetko, čo sa musí skrývať za rozhraním, stále potrebuje starý prístup.

Štandardná knižnica to už využila. `Rand` z `math/rand/v2` dostal generickú metódu:

```go
func (r *Rand) N[Int intType](n Int) Int
```

```go
r := rand.New(rand.NewPCG(1, 2))
fmt.Println(r.N(int32(100)))     // 76
fmt.Println(r.N(time.Second))    // 616.436223ms
```

Predtým bola generická iba funkcia na úrovni balíka `rand.N`, ktorá používala globálny zdroj. Teraz si vlastný, seedovaný `*Rand` získa rovnaké pohodlie. Malá vec. Pekná vec.

### 1.2. Selektory polí v literáloch štruktúr, alebo: Problém #9859 konečne dostáva pokoj

Vnorenie vám dáva `u.ID`. Vnorenie vám _nedáva_ `User{ID: 1}`. Namiesto toho dostanete `User{Base: Base{ID: 1}}`, čo je spôsob, akým sa Go pýta, či ste to naozaj mysleli.

[Problém #9859](https://go.dev/issue/9859) bol založený v roku 2015. Teraz je uzavretý. Niekde jeden gopher, ktorý sa medzitým dvakrát prekvalifikoval, dostáva notifikáciu z GitHubu.

```go
type Object struct{ name, color string }
type Point3D struct {
	Object
	x, y, z float64
}
type Line struct {
	Object
	p, q Point3D
}

// Go 1.27: použite povýšené pole priamo
line := Line{name: "diagonal", q: Point3D{y: -4, z: 12.3}}
```

`name` nie je poľom `Line`. Je poľom vnoreného `Object`. Predtým ste museli napísať `Line{Object: Object{name: "diagonal"}, ...}`.

Teraz drobné podrobnosti, ktoré som overil skutočným pokusom o kompiláciu všetkého.

**(a) Kľúčom je stále jednoduchý identifikátor.** Nemôžete zapísať ľubovoľnú cestu selektora.

```go
_ = Line{Object.name: "x"}   // invalid field name Object.name in struct literal
_ = Line{p.x: 1}             // invalid field name p.x in struct literal
```

Funkcia teda nie je v tom, že kľúče fungujú ako prístup k poliam. Ide konkrétne o to, že sú povolené implicitne povýšené názvy polí. Je to o niečo užšie, než naznačuje titulok.

**(b) Nemôžete súčasne uviesť vnorené pole a niečo, čo bolo z neho povýšené.**

```go
obj := Object{"edge", "black"}
_ = Line{Object: obj, name: "diagonal"}
// cannot specify promoted field name and enclosing embedded field Object
```

Rozumné. Povedali ste mu dve protichodné veci o tej istej pamäti a on odmietol hádať.

**(c) Vnorenie ukazovateľov nie je vítané.**

```go
type PtrEmbed struct {
	*Object
	z int
}

_ = PtrEmbed{name: "x"}
// invalid implicit pointer indirection to reach name
```

Aby ste mohli nasledovať ukazovateľ, potrebujete ukazovateľ, ktorý nasledujete, a v čase konštrukcie literálu ešte neexistuje. Aj to je férové.

Dobrá správa: `go fix` vaše staré literály prepíše automaticky. K tomu sa dostaneme neskôr.

### 1.3. Odvodzovanie typov funkcií je menej svojvoľné

Odvodzovanie typov pre generické funkcie fungovalo kedysi na niektorých miestach a na iných nie, bez akéhokoľvek rozpoznateľného princípu, ktorý určoval, ktoré je ktoré. Priradenie do premennej? V poriadku. Umiestnenie úplne rovnakej funkcie do poľa štruktúry? Chyba kompilátora, prosím napíšte `double[int]`, ako keby bol rok 2022.

Go 1.27 zabezpečuje, že odvodzovanie funguje **v každom kontexte, kde je cieľový typ jednoznačne známy**:

```go
func double[T ~int | ~float64](v T) T { return v * 2 }

type S struct{ f func(int) int }
type A [1]func(int) int

func main() {
	s := S{f: double}          // 1.26: vyžadovalo double[int]
	a := A{double}             // 1.26: vyžadovalo double[int]

	c := make(chan func(int) int, 1)
	c <- double                // 1.26: vyžadovalo double[int]

	var fn func(float64) float64 = double  // toto fungovalo vždy

	fmt.Println(s.f(21), a[0](5), (<-c)(7), fn(1.5))
	// 42 10 14 3
}
```

Jedna funkcia `double` sa inštanciuje ako `func(int) int` na troch miestach a ako `func(float64) float64` na štvrtom, a to výhradne z kontextu. Ak píšete štruktúry s možnosťami alebo tabuľky handlerov plné funkčných hodnôt, množstvo šumu `[T]` sa z vašej kódovej základne čoskoro vytratí.

## 2. Runtime: Bezplatný výkon a detektor únikov

### 2.1. Alokácia špecializovaná podľa veľkosti

Kompilátor teraz generuje volania alokačných rutín špecializovaných podľa veľkosti pre malé objekty. Poznámky k vydaniu sľubujú až 30 % úsporu pri alokáciách pod 80 bajtov a približne 1 % celkovo pre programy náročné na alokácie.

Poznámkam k vydaniu neverím bez merania, takže:

```go
type small struct{ a, b, c, d int }

var sink any

func BenchmarkAlloc(b *testing.B) {
	for b.Loop() {
		sink = &small{}
	}
}

func BenchmarkSlice(b *testing.B) {
	for b.Loop() {
		sink = make([]byte, 48)
	}
}
```

```
# predvolené (size-specialized malloc zapnutý)
BenchmarkAlloc-12     3000000     5.358 ns/op
BenchmarkSlice-12     3000000    13.58 ns/op

# GOEXPERIMENT=nosizespecializedmalloc
BenchmarkAlloc-12     3000000     8.722 ns/op
BenchmarkSlice-12     3000000    20.81 ns/op

```

V mikrobenchmarku, ktorý nerobí nič iné než alokácie na Apple Silicon, je to o 30–35 % rýchlejšie. Inými slovami: ide o najlepší prípad, dosiahnutý v laboratórnych podmienkach benchmarkom navrhnutým tak, aby číslo vyzeralo dobre. V reálnom programe väčšinu tohto zisku pohltí GC a skutočná práca. Tvrdenie o ~1 % je to čestné.

Cenou je veľkosť binárky. Hello World:

```
2413202 bytes  (predvolené)
2361826 bytes  (nosizespecializedmalloc)
```

Približne 50 KB, pevne, bez ohľadu na váš program. Môžete sa z toho odhlásiť pomocou `GOEXPERIMENT=nosizespecializedmalloc`, ale **táto únikovú cesta má byť odstránená v Go 1.28**, takže ju berte ako obchádzku pre hlásenie chyby, nie ako životný štýl.

### 2.2. Profil únikov goroutín je teraz skutočný

V Go 1.26 experimentálny, v 1.27 všeobecne dostupný. GOEXPERIMENT `goroutineleakprofile` zanikol; jednoducho funguje.

```go
func leak() {
	ch := make(chan int) // nikto nikdy nepošle. nikto.
	go func() {
		<-ch
	}()
}

func main() {
	for range 3 {
		leak()
	}
	runtime.GC()
	runtime.GC()
	pprof.Lookup("goroutineleak").WriteTo(os.Stdout, 1)
}
```

```
goroutineleak profile: total 3
3 @ 0x104de3f18 0x104d7f0b0 0x104d7ec34 0x104e374f4 0x104dea024
#	0x104e374f3	main.leak.func1+0x23	/tmp/go127/leak/main.go:12
```

Tri goroutiny natrvalo uviaznuté, so súborom a číslom riadka ukazujúcimi presne na miesto, kde ste to spravili.

Trik za tým je naozaj vynikajúci: **znovu používa analýzu dosiahnuteľnosti garbage collectora**. Ak je goroutina G blokovaná na primitíve P a P nie je dosiahnuteľný z nijakej spustiteľnej goroutiny (ani z ničoho, čo by tieto goroutiny mohli zobudiť), potom sa P nikdy nemôže znovu dotknúť nič, a G sa preto nikdy nezobudí. Nie je to heuristika, je to dôkaz.

Rovnaký návrh vám poskytuje aj obmedzenie zadarmo: ak je kanál alebo mutex dosiahnuteľný cez globálnu premennú alebo cez lokálnu premennú nejakej stále bežiacej goroutiny, GC ho stále vidí, takže runtime nemôže nič vyvodiť. Všimnite si, že aj vyššie uvedený hračkársky príklad potrebuje na nahlásenie dve volania `runtime.GC()`. Nezachytí všetko.

Zachytí však klasické „zabudol som zrušiť kontext, pracovná goroutina žije večne“, čo je väčšina únikov väčšinu času.

Ak importujete `net/http/pprof`, je dostupný aj na `/debug/pprof/goroutineleak`. Pripojte to na staging, kontrolujte to raz týždenne a buďte potichu zdesení.

Túto prácu prispel Vlad Saioc z Uberu, ktorý si zaslúži nápoj podľa vlastného výberu.

### 2.3. Tracebacky teraz povedia, ktorá požiadavka zlyhala

Pre moduly deklarujúce Go 1.27 alebo novší sa hlavičkové riadky tracebackov teraz zobrazujú spolu s návestiami goroutín z `runtime/pprof`.

```go
func handle(ctx context.Context) {
	var wg sync.WaitGroup
	wg.Go(func() {
		buf := make([]byte, 4<<10)
		os.Stdout.Write(buf[:runtime.Stack(buf, true)])
	})
	wg.Wait()
}

func main() {
	labels := pprof.Labels("request", "GET /orders/42", "tenant", "acme")
	pprof.Do(context.Background(), labels, handle)
}
```

```
goroutine 3 [running] {request: "GET /orders/42", tenant: acme}:
main.handle.func1()
	/tmp/go127/tblabel/main.go:15 +0x40
sync.(*WaitGroup).Go.func1()
	...
goroutine 1 [sync.WaitGroup.Wait] {request: "GET /orders/42", tenant: acme}:
...
```

Dôležitý detail: **návestia sa dedia do podriadených goroutín.** Goroutina 3 nikdy nenastavila návestie. Dostala ho od svojho rodiča.

Takže keď sa produkcia nabudúce zasekne a niekto pošle procesu SIGQUIT, namiesto 4 000 goroutín, ktoré vyzerajú všetky rovnako, dostanete 4 000 goroutín označených tým, ku ktorej požiadavke a ku ktorému tenantovi patria. Tri riadky `pprof.Do` na vstupnom bode vašich požiadaviek vám to zabezpečia.

Keďže návestia môžu obsahovať veci, ktoré by ste nechceli vypisovať na stderr, `GODEBUG=tracebacklabels=0` to vypne, a táto možnosť odhlásenia má explicitne zostať navždy.

### 2.4. asynctimerchan je navždy preč

Go 1.23 urobil kanály časovačov nebufrovanými (synchrónnymi) a ponúkol `asynctimerchan=1` ako spôsob návratu k starému správaniu. V 1.27 je toto nastavenie **natrvalo odstránené**. Kanály balíka `time` sú synchrónne, bez akýchkoľvek výnimiek.

Zaujímavá je politika, ktorá sa s tým zaviedla. Odstránený GODEBUG ponechaný vo vašom `go.mod` automaticky nerozbije zostavenie: rozbije ho iba vtedy, ak je nastavený na **starú** hodnotu:

```
# go.mod obsahuje: godebug asynctimerchan=1
go: error loading go.mod:
go.mod:5: removed GODEBUG "asynctimerchan" set to old value "1" (https://go.dev/doc/godebug#go-127)

# go.mod obsahuje: godebug asynctimerchan=0
(builds fine)
```

To znamená, že kričať na vás budú iba ľudia, ktorí sa skutočne spoliehali na odstránené správanie. Všetci, ktorí to nastavili na neskoršiu predvolenú hodnotu a zabudli na to, môžu ďalej nemyslieť na to. Je to premyslený kus archeológie API.

## 3. Štandardná knižnica

### 3.1. encoding/json/v2: Motor vymenili za chodu

Toto je veľká zmena. [Viac než dva roky diskusie o návrhu](https://go.dev/issue/71497) sa konečne zhmotnilo.

Teraz existujú **tri balíky** a pochopenie rozdelenia je väčšinou bitky:

| Balík                    | Úloha                                                                          |
| ------------------------ | ------------------------------------------------------------------------------ |
| `encoding/json`          | API v1, ktoré poznáte. **Správanie je na 100 % nezmenené.** Teraz implementované nad v2 |
| `encoding/json/v2`       | Sémantické spracovanie. Hodnoty Go ↔ JSON                                      |
| `encoding/json/jsontext` | Syntaktické spracovanie. JSON ako prúd tokenov                                 |

Hlavnou správou je, že `encoding/json` bol prestavaný na úplne inú implementáciu a **správa sa identicky**. Ako to funguje:

```go
// encoding/json.Unmarshal v Go 1.27, skrátene
func Unmarshal(data []byte, v any) error {
	return jsonv2.Unmarshal(data, v, DefaultOptionsV1())
}
```

Každá staršia zvláštnosť v1 bola zakódovaná ako možnosť, zabalená do `DefaultOptionsV1()`, a API v1 ju vždy aplikuje. To znamená, že toto sa správa rovnako v 1.26 aj 1.27:

```go
var m map[string]int
json.Unmarshal([]byte(`{"a":1,"a":2}`), &m)  // <nil>, map[a:2]
```

Áno, v1 stále potichu prijíma duplicitné kľúče a berie posledný. Vždy to robil. Stále to robí. Kompatibilita znamená kompatibilitu aj so zlými časťami.

**v2 medzitým má názory:**

```go
var m map[string]int
jsonv2.Unmarshal([]byte(`{"a":1,"a":2}`), &m)
// jsontext: duplicate object member name "a"

var s string
jsonv2.Unmarshal([]byte("\"\xff\""), &s)
// jsontext: invalid UTF-8 after offset 1
```

Duplicitné kľúče a neplatné UTF-8 sú odmietnuté. Obe sú skutočne nebezpečné, nielen neupravené: keď sa dva parsery nezhodnú v tom, ktorý duplicitný kľúč vyhrá, vznikajú bezpečnostné chyby. CouchDB CVE-2017-12635 bol presne tento prípad: JSON telo s dvoma kľúčmi `roles`, kde validátor čítal jeden a úložná vrstva druhý. Odmietnutie je správne rozhodnutie.

Možnosti sú variadické:

```go
type Config struct {
	Name    string   `json:"name"`
	Tags    []string `json:"tags,omitzero"`
	Timeout int      `json:"timeout,omitzero"`
}

out, _ := jsonv2.Marshal(Config{Name: "api"})
// {"name":"api"}

// zoradené kľúče mapy + odsadenie
out, _ = jsonv2.Marshal(map[string]int{"b": 2, "a": 1},
	jsonv2.Deterministic(true), jsontext.WithIndent("  "))
// {
//   "a": 1,
//   "b": 2
// }

// odmietnutie neznámych polí — predtým sa vyžadoval Decoder a DisallowUnknownFields()
var c Config
err := jsonv2.Unmarshal([]byte(`{"name":"api","nope":1}`), &c,
	jsonv2.RejectUnknownMembers(true))
// json: cannot unmarshal JSON string into Go main.Config:
//   unknown object member name "nope"
```

`Deterministic`, `MatchCaseInsensitiveNames`, `StringifyNumbers`, `FormatNilSliceAsNull`, `OmitZeroStructFields`: väčšina vecí, ktoré ste predtým riešili knižnicou tretej strany alebo ručne napísaným `MarshalJSON`, je teraz príznakmi.

A príbeh migrácie je tou najlepšou časťou. **Neskoršie možnosti majú prednosť**, takže prísnosť v2 môžete prijať po jednom správaní:

```go
// zachovať sémantiku v1, ale odmietnuť duplicitné kľúče ako v2
jsonv2.Unmarshal(data, &v,
	json.DefaultOptionsV1(),
	jsontext.AllowDuplicateNames(false))
// duplicate object member name
```

Kvôli zastaveniu prijímania duplicitných kľúčov nemusíte portovať 200-tisícriadkovú kódovú základňu do v2. Prepnete jednu možnosť. Pri akomkoľvek väčšom projekte je to realistická cesta.

Výkon: marshalovanie je zhruba na úrovni pôvodného, **unmarshalovanie je výrazne rýchlejšie.** Ak sa niečo pokazí, `GOEXPERIMENT=nojsonv2` obnoví starú implementáciu. Aj táto možnosť odhlásenia je časom na odstránenie, takže radšej založte issue, než sa tomu prispôsobujte.

`jsontext` je nízkoúrovňová vrstva: `Encoder`/`Decoder` prechádzajú JSON ako `Token` a `Value` so stavovým automatom, ktorý vás drží v intelektuálnej poctivosti. Siahnite po ňom, keď píšete streamovaný transformátor alebo filter JSON a nechcete vôbec materializovať hodnoty Go.

### 3.2. Štandardný balík uuid

Konečne. RFC 9562 v štandardnej knižnici, bez potreby `go get`.

```go
import "uuid"

func main() {
	fmt.Println(uuid.NewV4())
	// b97aa695-da08-472c-af81-ff088129019f

	// v7: horných 48 bitov je časová pečiatka, takže tieto vždy zoradia v poradí vzniku
	a, b := uuid.NewV7(), uuid.NewV7()
	fmt.Println(a.Compare(b) < 0) // true

	u, err := uuid.Parse("urn:uuid:0198a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b")
	fmt.Println(u, err)
	// 0198a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b <nil>

	fmt.Println(uuid.Nil(), uuid.Max())
	// 00000000-0000-0000-0000-000000000000 ffffffff-ffff-ffff-ffff-ffffffffffff
}
```

Celé API tvoria `New`, `NewV4`, `NewV7`, `Nil`, `Max`, `Parse`, `MustParse` a jeden typ: `type UUID [16]byte`. To je všetko. Celú dokumentáciu balíka si môžete prečítať, kým vám vychladne káva.

Podrobnosti, ktoré stojí za to vedieť:

- **`UUID` je `[16]byte`**, takže funguje `==` a priamo sa dá použiť ako kľúč mapy. Rovnaký návrh ako `google/uuid`.
- **`Nil` a `Max` sú funkcie, nie premenné.** Lebo balíková `var Nil UUID` je nabitá zbraň namierená na vašu nohu a niekto niekde by do nej nakoniec priradil hodnotu.
- Náhodné bity pochádzajú z **kryptograficky bezpečného** generátora.
- Implementuje `encoding.TextMarshaler`/`TextUnmarshaler`/`TextAppender`, takže sa bez problémov zmestí do štruktúr JSON.
- **`NewV7` je ten, ktorý chcete pre primárne kľúče databázy.** Časové zoradenie znamená, že váš B-strom index prestane fragmentovať, čo sú UUID v4 preslávene zlé.

Teraz môžete odstrániť jednu závislosť. Ak potrebujete v1/v3/v5 alebo pokročilejšie možnosti parsovania, knižnice tretích strán stále majú svoje miesto.

### 3.3. crypto/mldsa: Post-kvantové podpisy, a sú obrovské

Go 1.24 nám dal `crypto/mlkem` pre post-kvantovú výmenu kľúčov. Go 1.27 prináša druhú polovicu: podpisy ML-DSA, štandardizované ako FIPS 204.

```go
sk, _ := mldsa.GenerateKey(mldsa.MLDSA65())
pk := sk.PublicKey()

msg := []byte("release the gophers")
opts := &mldsa.Options{Context: "gosuda.org/blog"}

sig, _ := sk.Sign(rand.Reader, msg, opts)

fmt.Println("private key seed:", len(sk.Bytes()), "bytes")  // 32
fmt.Println("public key:", len(pk.Bytes()), "bytes")        // 1952
fmt.Println("signature:", len(sig), "bytes")                // 3309

fmt.Println(mldsa.Verify(pk, msg, sig, opts))
// <nil>
fmt.Println(mldsa.Verify(pk, msg, sig, &mldsa.Options{Context: "other"}))
// mldsa: invalid signature
```

Pozrite sa na tieto čísla. **Jediný podpis má 3 309 bajtov.** Ed25519 má 64. To je 50-násobný nárast a verejný kľúč má k tomu takmer 2 KB. Napchajte niekoľko takýchto podpisov do reťaze certifikátov a váš handshake TLS začne potrebovať vlastnú stratégiu MTU.

Toto je skutočná cena kvantovej odolnosti dnes a preto sa zatiaľ nikto nepresúva so všetkým hneď zajtra. Ale je už v štandardnej knižnici, kde ju chcete mať _predtým_, než ju potrebujete.

`Options.Context` slúži na oddelenie domén: podpíšte rovnakým kľúčom pre rôzne účely, pre každý použite iný kontext a podpis z jedného kontextu sa v inom neoverí. Príklad vyššie to presne ukazuje: rovnaký kľúč, rovnaká správa, iný kontext, odmietnutie.

`PrivateKey` implementuje `crypto.Signer`, takže zapadá do existujúcich rozhraní. `crypto/x509` spracúva kľúče a podpisy ML-DSA a `crypto/tls` podporuje schémy podpisov `MLDSA44`/`MLDSA65`/`MLDSA87` v TLS 1.3.

Existuje aj `SignDeterministic`, ktorý preskakuje náhodnosť, čo je užitočné pre testy a reprodukovateľné zostavenia.

### 3.4. simd: Vektorové inštrukcie bez assembleru

Go 1.26 zaviedol architektúre špecifický `simd/archsimd` ako experiment. Go 1.27 pridáva `simd`, ktorý je **prenosný a nezávislý od šírky vektora**. Aktivuje sa pomocou `GOEXPERIMENT=simd`.

```go
// dst = a*x + y
func axpy(dst, x, y []float32, a float32) {
	va := simd.BroadcastFloat32s(a)
	w := va.Len()

	i := 0
	for ; i+w <= len(x); i += w {
		vx := simd.LoadFloat32s(x[i:])
		vy := simd.LoadFloat32s(y[i:])
		vx.MulAdd(va, vy).Store(dst[i:])
	}
	// spracovanie repu čiastočným načítaním/uložením — žiadna skalárna čistiaca slučka
	if i < len(x) {
		vx, _ := simd.LoadFloat32sPart(x[i:])
		vy, _ := simd.LoadFloat32sPart(y[i:])
		vx.MulAdd(va, vy).StorePart(dst[i:])
	}
}
```

```bash
$ GOEXPERIMENT=simd go1.27rc3 run ./simddemo
vector bits: 128 emulated: false
[4 7 10 13 16 19 22 25 28 31]
```

Dôležité návrhové rozhodnutie: **šírka vektora nie je nikdy pevne zakódovaná.** Hodnotu zisťujete pomocou `va.Len()` za behu. `VectorBitSize()` vám povie skutočnú šírku, `Emulated()` povie, či máte skutočný hardvér alebo zdvorilú softvérovú imitáciu. Môj Mac hlásil 128-bitový NEON; stroj s AVX-512 hlási viac; stroj bez podpory hlási emuláciu a kód stále beží.

`LoadFloat32sPart`/`StorePart` si zaslúžia osobitnú zmienku. Najotravnejšou časťou ručne písaného SIMD je vždy nepravidelný koniec poľa a toto to rieši bez samostatnej skalárnej slučky.

Stále experimentálne, API stále nestabilné, nenasadzujte to do produkcie. Ale skutočnosť, že ide o kód v Go a nie o assembler alebo cgo, je naozaj veľký posun.

### 3.5. hash/maphash.Hasher

Nové rozhranie, ktoré popisuje kontrakt medzi hodnotovým typom a kontajnermi založenými na hašovaní:

```go
type Hasher[T any] interface {
	Hash(*Hash, T)
	Equal(x, y T) bool
}
```

Prečo? Vstavaná mapa Go prijíma iba kľúče `comparable`. Nemôžete použiť slice ako kľúč. Nemôžete definovať „rovnosť bez ohľadu na veľkosť písmen“. `Hasher` rieši obe veci naraz:

```go
type CaseInsensitive struct{}

func (CaseInsensitive) Hash(h *maphash.Hash, s string) {
	h.WriteString(strings.ToLower(s))
}
func (CaseInsensitive) Equal(x, y string) bool {
	return strings.ToLower(x) == strings.ToLower(y)
}

var seed = maphash.MakeSeed()

func hashOf[T any](hr maphash.Hasher[T], v T) uint64 {
	var h maphash.Hash
	h.SetSeed(seed)   // rovnaký seed, inak tento celý výpočet nič neznamená
	hr.Hash(&h, v)
	return h.Sum64()
}

fmt.Println(hashOf(CaseInsensitive{}, "Go") == hashOf(CaseInsensitive{}, "GO"))
// true
```

Pre bežnú sémantiku `==` existuje `ComparableHasher[T]`:

```go
hashOf(maphash.ComparableHasher[int]{}, 42)
```

Záludnosť: **`Hasher` je rozhranie, nie dátová štruktúra.** Zatiaľ neexistuje žiadna štandardná hašovacia tabuľka ani Bloomov filter, ktorý by ho využíval. Ide o základ pre budúci balík kontajnerov. Dnes by ste ho použili pri budovaní vlastnej štruktúry alebo pri použití existujúcej implementácie, napríklad `go/types.Hasher` (ktorý vám umožňuje použiť `types.Type` ako kľúč mapy s rešpektovaním `Identical`).

Správa seedov je na vás. Ak by `hashOf` vyššie vytvárala pri každom volaní nový seed, rovnaké hodnoty by sa hašovali odlišne a príklad by vypísal `false`, presne ako chyba, ktorú som urobil pri prvom pokuse. Jeden seed na jeden kontajner. (Seed je náhodný, aby sa zabránilo útokom DoS zahlcovaním hašov, a preto nie je len konštantou.)

### 3.6. httptest.NewTestServer + synctest.Sleep: Úder spiča

Ak z tohto vydania prijmete len jednu vec, nech je to táto.

`testing/synctest` sa dostal do stavu stabilnej funkcie v Go 1.25 a poskytol nám falošné hodiny na testovanie súbežného kódu. Avšak v momente, keď sa váš test dotkol skutočnej siete, iluzívny svet sa zrútil. `httptest.NewTestServer` z Go 1.27 používa **pamäťovú falošnú sieť**, takže bublina zostáva nedotknutá. A `synctest.Sleep` (= `time.Sleep` + `synctest.Wait`) to dopĺňa.

Tu je test opakovania s exponenciálnym backoffom:

```go
func TestRetryWithBackoff(t *testing.T) {
	synctest.Test(t, func(t *testing.T) {
		var hits int
		srv := httptest.NewTestServer(t, http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				hits++
				if hits < 3 {
					w.WriteHeader(http.StatusServiceUnavailable)
					return
				}
				io.WriteString(w, "ok")
			}))

		client := srv.Client()
		start := time.Now()
		var body string
		for attempt := range 5 {
			resp, err := client.Get(srv.URL)
			if err != nil {
				t.Fatal(err)
			}
			b, _ := io.ReadAll(resp.Body)
			resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				body = string(b)
				break
			}
			synctest.Sleep(time.Duration(1<<attempt) * time.Second)
		}

		if body != "ok" {
			t.Fatalf("got %q", body)
		}
		// backoff má byť presne 1s + 2s = 3s
		if elapsed := time.Since(start); elapsed != 3*time.Second {
			t.Fatalf("elapsed = %v, want 3s", elapsed)
		}
		t.Logf("hits=%d elapsed=%v", hits, time.Since(start))
	})
}
```

```
=== RUN   TestRetryWithBackoff
    x_test.go:48: hits=3 elapsed=3s
--- PASS: TestRetryWithBackoff (0.00s)
```

Prečítajte si to dvakrát. Simuloval tri sekundy backoffu proti skutočnému HTTP serveru a skončil za **0.00 sekúnd**. A tvrdenie je `elapsed == 3*time.Second`, teda _presne_ tri sekundy, nie „aspoň tri sekundy, plus-mínus jitter plánovača“. Falošné hodiny nemajú jitter.

`synctest.Sleep` existuje z konkrétneho dôvodu: ak váš test spí rovnako dlho ako testovaný kód, nikto nevie, ktorý sa zobudí prvý. `synctest.Sleep` spí _a potom_ čaká, kým sa všetky ostatné goroutiny v bublina trvalo nezablokujú, takže pozorujete systém až potom, čo sa ustálil.

Časové limity, opakovania, circuit breakery, rate limitery: každý časovo závislý test HTTP klienta, ktorý vlastníte, sa môže stať rýchlym a deterministickým. Ak je váš testovací balík momentálne držaný pokope pomocou `time.Sleep(100 * time.Millisecond)` a nádeje, toto je vaša cesta von.

### 3.7. Zmeny v net/http, ktoré skutočne ovplyvnia produkciu

Tiché, ale objavia sa vo vašich metrikách.

**Tela odpovedí HTTP/1 sa pri Close automaticky vyprázdňujú.** Neprečítaný obsah sa teraz vyprázdni (až do konzervatívneho limitu), keď zatvoríte telo, takže spojenie sa môže znovu použiť. Čo znamená, že konečne môžete zmazať toto zaklínadlo, ktoré si všetci kopírujú z tej istej odpovede na Stack Overflow od roku 2016:

```go
// už nie je potrebné
defer func() {
	io.Copy(io.Discard, resp.Body)
	resp.Body.Close()
}()
```

Pre väčšinu programov je to bez efektu alebo malý zisk. Ak to veci _zhorší_, pravdepodobne ste v skupine, ktorú poznámky k vydaniu zdvorilo opisujú: `Transport.MaxIdleConns` nastavené na 0 alebo nový `Client` pre každú požiadavku, čím sa úplne obíde limit nečinných spojení. `Transport.DisableKeepAlives = true` to zamaskuje, ale skutočná rada z poznámok k vydaniu je, že „hlbší pohľad by pravdepodobne bol prospešný“, čo je v jazyku tímu Go preklad: _máte väčšie problémy_.

**Priorita klienta HTTP/2 (RFC 9218).** Server teraz rešpektuje signály priority od klienta. Ak ste preferovali starý round-robin plánovač, nastavte `Server.DisableClientPriority = true`.

**`Server.MaxHeaderValueCount`.** Obmedzuje počet hodnôt hlavičiek, ktoré server prijme, predvolene `DefaultMaxHeaderValueCount`. Ďalšie zatvorené dvere pre „pošli desaťtisíc hlavičiek a sleduj, čo sa stane“.

**ALPN na používateľsky poskytnutých spojeniach.** Ak vaše `net.Conn` implementuje `ConnectionState() tls.ConnectionState`, `Transport` a `Server` na ňom vykonajú vyjednávanie TLS ALPN, takže vlastné dialery, ktoré idú cez proxy, môžu stále vyjednať HTTP/2.

### 3.8. Drobnosti, ktoré prinášajú radosť

**`strings.CutLast` / `bytes.CutLast`.** `Cut` rozdelí na _prvom_ oddeľovači. Variant „last“ neexistoval, takže každý ručne písal `LastIndex` plus rezanie a zhruba 30 % z nás na prvý pokus spravilo chybu o jedno.

```go
name, ext, ok := strings.CutLast("archive.tar.gz", ".")
// archive.tar gz true
```

Skvelé pre prípony súborov a pre parsovanie `host:port`, kde musíte nájsť _posledný_ dvojbodku, lebo adresy IPv6 ich obsahujú plno.

**`math/big.Int.Divide`.** Delenie s explicitným režimom zaokrúhľovania, ktoré nahrádza hádzanie mincou „chcem `Quo` alebo `Div`?“:

```go
x, y := big.NewInt(-7), big.NewInt(2)

new(big.Int).Divide(x, y, new(big.Int), big.Trunc)  // q=-3 r=-1
new(big.Int).Divide(x, y, new(big.Int), big.Floor)  // q=-4 r=1
new(big.Int).Divide(x, y, new(big.Int), big.Round)  // q=-4 r=1
new(big.Int).Divide(x, y, new(big.Int), big.Ceil)   // q=-3 r=-1
```

Ak pracujete v doméne, kde je pravidlo zaokrúhľovania zapísané v nariadení, je toto pre vás.

**`url.URL.Clone` a `url.Values.Clone`.** Hlboké kópie. Staré plytké kopírovanie `*u` zdieľalo ukazovateľ `Userinfo`, čo vytváralo presne taký druh chyby, ktorej hľadanie zaberie deň a oprava päť sekúnd.

```go
orig, _ := url.Parse("https://gosuda.org/blog?tag=go&tag=1.27")
clone := orig.Clone()
clone.Host = "example.com"
fmt.Println(orig.Host, clone.Host)  // gosuda.org example.com
```

Metódy čítania **`net.UnixConn`** teraz vracajú `io.EOF` priamo namiesto jeho obalenia do `net.OpError`. `err == io.EOF` funguje teraz, tak ako malo fungovať vždy.

**`database/sql.ConvertAssign` a `driver.RowsColumnScanner`.** Funkcie pre autorov ovládačov. Prvá sprístupňuje konverzie typov, ktoré vykonáva `Rows.Scan`; druhá umožňuje ovládačom skenovať priamo do cieľov používateľa a preskočiť medziľahlú alokáciu.

**`unicode` 15 → 17.** Dve verzie naraz. Klasifikácia reťazcov a správanie normalizácie sa môžu jemne posunúť. Ak máte testy, ktoré na tom závisia, zistíte to.

**`compress/flate` je rýchlejší** a **jeho výstupné bajty sa môžu líšiť od Go 1.26.** Toto sa prenáša na `archive/zip`, `compress/gzip`, `compress/zlib` a `image/png`. Ak máte testy so zlatými súbormi, ktoré hašujú komprimovaný výstup, rozbijú sa a nebude to regresia. Skontrolujte to _pred_ aktualizáciou, nie počas revízie incidentu.

## 4. Toolchain

### 4.1. go fix získal ďalšie modernizátory

`go fix` sa potichu mení na nástroj na modernizáciu kódu. Go 1.27 pridáva `atomictypes`, `embedlit`, `slicesbackward` a `unsafefuncs`. Použite `-diff` na náhľad:

```go
package fixdemo

import (
	"sync"
	"sync/atomic"
)

type Base struct{ ID int }
type User struct {
	Base
	Name string
}

func New() User {
	return User{Base: Base{ID: 1}, Name: "gopher"}
}

var counter int64

func Incr() { atomic.AddInt64(&counter, 1) }

func Reverse(s []string) {
	for i := len(s) - 1; i >= 0; i-- {
		_ = s[i]
	}
}

func Spawn(wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		defer wg.Done()
	}()
}
```

```bash
$ go1.27rc3 fix -diff ./fixdemo
```

```diff
 import (
+	"slices"
 	"sync"
 	"sync/atomic"
 )

 func New() User {
-	return User{Base: Base{ID: 1}, Name: "gopher"}
+	return User{ID: 1, Name: "gopher"}
 }

-var counter int64
+var counter atomic.Int64

-func Incr() { atomic.AddInt64(&counter, 1) }
+func Incr() { counter.Add(1) }

 func Reverse(s []string) {
-	for i := len(s) - 1; i >= 0; i-- {
-		_ = s[i]
+	for _, v := range slices.Backward(s) {
+		_ = v
 	}
 }

 func Spawn(wg *sync.WaitGroup) {
-	wg.Add(1)
-	go func() {
-		defer wg.Done()
-	}()
+	wg.Go(func() {
+	})
 }
```

Na jednom malom súbore sa spustili štyri modernizátory:

- `embedlit`: prepisuje vnorené literály na novú syntax z §1.2
- `atomictypes`: `atomic.AddInt64(&x, 1)` sa mení na `atomic.Int64.Add(1)`, čo znemožňuje neatomický prístup a potichu opravuje chyby zarovnania na 32-bitových systémoch, o ktorých ste nevedeli
- `slicesbackward`: spätné slučky sa menia na `slices.Backward`
- `waitgroupgo`: rituál `Add`/`go`/`Done` sa mení na `wg.Go` (premenované z `waitgroup` z 1.26 kvôli nejednoznačnosti)

`go tool fix help` vypíše všetkých 26; `go tool fix help <name>` vysvetlí jeden. Okrem toho bol `fmtappendf` odstránený „z dôvodu štylistických obáv“, čo je nádherne diplomatický spôsob, ako opísať to, čo sa stalo vo vlákne tej issue.

Spustenie `go fix ./...` na starej kódovej základni je naozaj uspokojivé popoludnie. Samozrejme, najprv si prečítajte diff.

### 4.2. go test predvolene spúšťa stdversion

Toto je zmena, ktorá s najväčšou pravdepodobnosťou preruší váš deň.

`go test` teraz predvolene spúšťa kontrolu vet `stdversion`, ktorá označí symboly štandardnej knižnice novšie, než dovoľuje direktíva `go` v `go.mod`:

```go
// go.mod hovorí: go 1.24
package sv

import "strings"

func Ext(name string) string {
	_, ext, _ := strings.CutLast(name, ".")  // CutLast je symbol z 1.27
	return ext
}
```

```
$ go1.27rc3 test ./...
# sv
./x.go:6:23: strings.CutLast requires go1.27 or later (module is go1.24)
FAIL	sv [build failed]
```

Toto eliminuje klasický scenár, keď všetko funguje na vašom počítači (najnovší toolchain) a v CI alebo na staršom prostredí používateľa to exploduje. Ak publikujete knižnice s konzervatívnou direktívou `go`, je to dar.

### 4.3. go test -json získal OutputType

Riadky `"Action":"output"` teraz nesú voliteľné pole `"OutputType"`: `"error"`, `"error-continue"` alebo `"frame"`.

```
'frame'  '=== RUN   TestFail\n'
None     '    x_test.go:6: hello\n'
'error'  '    x_test.go:7: boom\n'
'frame'  '--- FAIL: TestFail (0.00s)\n'
'frame'  'FAIL\n'
```

Výstup `t.Log` (bez poľa), výstup `t.Error` (`error`) a riadky generované frameworkom (`frame`) sa teraz dajú rozlíšiť. Každý, kto napísal regulárny výraz na zistenie, ktoré riadky výstupu testu sú „skutočné“, ho môže teraz zmazať.

### 4.4. Vylepšenia go doc

**Syntax `package@version`.** Prečítajte si dokumentáciu konkrétnej verzie bez toho, aby ste ju pridali do svojho modulu:

```bash
$ go1.27rc3 doc golang.org/x/sync/errgroup@v0.10.0
package errgroup // import "golang.org/x/sync/errgroup"
...
```

Praktické na kontrolu zmien pred aktualizáciou.

**`-ex` a výpis zdrojového kódu príkladov:**

```bash
$ go1.27rc3 doc -ex strings | grep Example
    func ExampleClone()
    func ExampleCompare()
    func ExampleContains()
    func ExampleCut()
    func ExampleCutPrefix()
    ...

$ go1.27rc3 doc strings.ExampleCut
package main

import (
	"fmt"
	"strings"
)

func main() {
	show := func(s, sep string) {
		before, after, found := strings.Cut(s, sep)
		fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
	}
	show("Gopher", "Go")
	...
}

Output:
Cut("Gopher", "Go") = "", "pher", true
```

Zdrojový kód príkladu _aj_ očakávaný výstup, v termináli, bez otvárania karty prehliadača, ktorá bude stále otvorená aj budúci štvrtok.

### 4.5. go mod tidy upratuje vaše bloky require

Pre moduly s `go 1.27` alebo novším `go mod tidy` zlúči rozptýlené bloky `require` najviac do dvoch (priame a nepriame), pričom zachová pripojené komentáre.

```go
// Pred
module tidydemo

go 1.27

require golang.org/x/sync v0.10.0

// networking
require golang.org/x/net v0.33.0

require (
	golang.org/x/sys v0.28.0 // indirect
)

require golang.org/x/text v0.21.0 // indirect
```

```go
// Po: go mod tidy
module tidydemo

go 1.27

require (
	// networking
	golang.org/x/net v0.33.0
	golang.org/x/sync v0.10.0
)
```

Všimnite si, že komentár `// networking` sa zachoval. Toto hlavne upratuje po **riešení konfliktov zlúčenia v Gite**, kde sa rodia toulavé bloky `require`. Ak má váš tím viac ako troch ľudí, ktorí pridávajú závislosti, konflikty vo vašom `go.mod` budú o niečo tichšie.

### 4.6. Rôzne drobnosti

- **Podpora `bzr` bola odstránená.** Ak sa vás to týka, naozaj by ma zaujímal váš príbeh.
- **`go tool trace -http` sa pri zadaní iba portu viaže na localhost.** `-http=:6060` už nepočúva na všetkých rozhraniach. Ak ste to mysleli vážne, použite `-http=0.0.0.0:6060`. Týmto sa správanie zhoduje s `go tool pprof` a zabraňuje to občasnému náhodnému verejnému profilovaču.
- **Súbory odpovedí (`@file`)** podporujú `compile`, `link`, `asm`, `cgo`, `cover` a `pack` v formáte kompatibilnom s GCC. Pre build systémy, ktoré prekračujú limity dĺžky príkazového riadka, ahoj, Bazel.

## 5. Kompilátor, linker, porty

**Kompilátor.** Relatívne názvy súborov v direktívach `//line` sa teraz riešia voči adresáru obsahujúceho súboru, v súlade s `go/scanner`. Relevantné, ak píšete generátory kódu.

Názvy symbolov funkčných literálov (uzáverov) sú tiež jednoduchšie: rovnaký názov bez ohľadu na inlining a viacero inštancií toho istého literálu môže zdieľať kód v binárke. Bez funkčnej zmeny, _až na_ to: kód, ktorý porovnáva identitu funkcií cez `reflect.Value.Pointer`, uvidí „rovnosť“ častejšie než predtým. Takéto porovnanie nikdy nebolo platné, ale ak ho máte, teraz začne klamať hlasnejšie.

**Linker.** Nové možnosti `-macos` a `-macsdk` nastavujú verzie OS a SDK v load command `LC_BUILD_VERSION` na macOS.

**Porty.**

- **Darwin teraz vyžaduje macOS 13 Ventura alebo novší**, ako bolo oznámené v 1.26. Skontrolujte svoje CI runnery.
- **PowerPC (`GOOS=linux GOARCH=ppc64`) prešiel na ABI ELFv2.** Vyžaduje Linux kernel 3.13+ (RHEL7 spätne portovaný na 3.10). Cgo, PIE a externé linkovanie sú teraz podporované. Ak používate cgo, ale potrebujete statickú čisto Go binárku, nastavte `CGO_ENABLED=0`.

## 6. Kontrolný zoznam aktualizácie

Go 1.27 berie kompatibilitu vážne, ale pred zvýšením verzie si skontrolujte tieto body:

- [ ] **Testy so zlatými súbormi nad komprimovaným výstupom.** `compress/flate` zmenil kodéry; bajty gzip/zip/png sa môžu líšiť.
- [ ] **Testy porovnávajúce reťazce chybových hlásení JSON.** Správanie je identické; **text chyby nie je**.
- [ ] **Odstránené GODEBUG v `go.mod`.** `asynctimerchan`, `gotypesalias`, `tlsrsakex`, `tls3des`, `tls10server`, `tlsunsafeekm`, `x509keypairleaf` — tieto zlyhajú zostavenie iba vtedy, ak sú nastavené na svoje _staré_ hodnoty.
- [ ] **Porušenia `stdversion`.** `go test` ich teraz zachytáva. Ak udržiavate knižnicu s konzervatívnou direktívou `go`, spúšťajte to včas.
- [ ] **CI runnery s macOS 12 alebo starším.** Podpora zanikla.
- [ ] **Testy tvrdiace niečo o názvoch symbolov funkčných literálov** a **porovnania funkcií cez `reflect.Value.Pointer`.**
- [ ] **`Transport.MaxIdleConns = 0`** alebo `Client` pre každú požiadavku. Spolu s automatickým vyprázdňovaním tiel odpovedí to môže spomaliť.

## Záverečné myšlienky

Go 1.27 je vydanie, kde naraz prišla splatná veľa odkladanej údržby.

Generické metódy boli chýbajúcim kúskom práce na generikách z 1.18. Selektory polí v literáloch štruktúr uzavreli jedenásťročnú otvorenú otázku. `encoding/json/v2` bol dva roky diskusie o návrhu. A `uuid` zaplnil dieru, ktorú takmer každý projekt v Go desaťročie zakrýval tou istou závislosťou tretej strany.

Ak chcete prioritné poradie pre skutočné získanie hodnoty z tohto vydania:

1. **`httptest.NewTestServer` + `synctest.Sleep`**: časovo závislé HTTP testy sa stanú rýchlymi a deterministickými. Najlepší pomer úsilia k výsledku v celom vydaní.
2. **Profil úniku goroutín**: vystavte jeden endpoint na stagingu a pripravte sa na pokoru.
3. **Návestia goroutín v tracebackoch**: tri riadky `pprof.Do` na vstupnom bode vašich požiadaviek zmenia váš ďalší výpis zásobníka o 3:00 ráno na čitateľnú vec.
4. **`go fix ./...`**: nechajte nástroj urobiť modernizáciu.
5. **`encoding/json/v2`**: nikam sa neponáhľajte. Prijmite ho po jednej možnosti.

Stiahnite si `go1.27rc3` a spustite na ňom svoj testovací balík ešte dnes. Všetko na tomto kontrolnom zozname sa dá príjemnejšie objaviť v utorok popoludní než počas incidentu.

**Referencie**

- [Go 1.27 Release Notes](https://go.dev/doc/go1.27)
- [The Go Programming Language Specification](https://go.dev/ref/spec)
- [proposal: encoding/json/v2 (#71497)](https://go.dev/issue/71497)
- [proposal: portable SIMD (#78902)](https://go.dev/issue/78902)
- [proposal: hash/maphash.Hasher (#70471)](https://go.dev/issue/70471)
