Go 1.27: Generic Methods, UUID, and Post-Quantum Crypto Arrive

Go 1.27 is shaping up to be a feature-packed release. The headline is generic methods, but there's also a standard UUID package, post-quantum signatures, JSON v2 by default, and faster memory allocation. Here's what changed, with runnable examples.

Generic Methods

Before Go 1.27, only top-level functions could be generic. Now methods can declare their own type parameters, independent of the receiver's. This lets you define generic operations directly on types.

type Box[T any] struct{ v T }

// The method declares its own type parameter U (new in Go 1.27).
func (b Box[T]) Map[U any](f func(T) U) Box[U] {
    return Box[U]{v: f(b.v)}
}

func main() {
    b := Box[int]{v: 21}
    doubled := b.Map(func(n int) int { return n * 2 })
    label := doubled.Map(func(n int) string {
        return fmt.Sprintf("value=%d", n)
    })
    fmt.Println(label.v)
}

There's a restriction: interfaces still can't declare type-parameterized methods. A generic method can't satisfy an interface.

type Mapper interface {
    Map[U any](f func(int) U) any // interface method must have no type parameters
}

Struct Literal Field Selectors

A key in a struct literal can now be any valid field selector, not just a top-level field name. This means you can set promoted fields directly without spelling out the embedded type.

type Base struct {
    ID int
}

type User struct {
    Base
    Name string
}

// Before: User{Base: Base{ID: 7}, Name: "Mittens"}
u := User{ID: 7, Name: "Mittens"}
fmt.Println(u.ID, u.Name)

Generalized Function Type Inference

Function type inference now works in more contexts, including conversions and composite literals. You no longer need to spell out type arguments when a generic function is used where a matching function type is expected.

func first[T any](s []T) T { return s[0] }
func last[T any](s []T) T  { return s[len(s)-1] }

ops := []func([]int) int{first, last} // T inferred as int for each
for _, op := range ops {
    fmt.Println(op([]int{10, 20, 30}))
}

Faster Memory Allocation

The compiler now generates calls to size-specialized memory allocation routines, cutting the cost of small (under 80 bytes) allocations by up to 30%. Overall gain is around 1% in allocation-heavy programs. Tradeoff: about 60 KB extra binary size. Opt out with GOEXPERIMENT=nosizespecializedmalloc (likely removed in Go 1.28).

Goroutine Labels in Tracebacks

For modules with go.mod set to Go 1.27+, tracebacks include pprof goroutine labels in the header line. This shows up in crash dumps, SIGQUIT traces, and runtime.Stack output.

ctx := context.Background()
pprof.Do(ctx, pprof.Labels("request", "42"), func(ctx context.Context) {
    buf := make([]byte, 1<<12)
    n := runtime.Stack(buf, false)
    fmt.Printf("%s", buf[:n])
})

Output includes {request: 42} after the goroutine state. Disable with GODEBUG=tracebacklabels=0.

Goroutine Leak Profile

The goroutineleak profile graduates from experiment to regular profile. It runs a GC cycle to find permanently blocked goroutines and reports their stacks.

func leak() {
    ch := make(chan int)
    ch <- 1 // blocks forever
}

go leak() runtime.Gosched() pprof.Lookup("goroutineleak").WriteTo(os.Stdout, 1)


Output shows `total 1` and the stack trace.

### Post-Quantum Signatures

The new `crypto/mldsa` package implements ML-DSA (FIPS 204). Three parameter sets: MLDSA44, MLDSA65, MLDSA87.

```go
priv, _ := mldsa.GenerateKey(mldsa.MLDSA65())
msg := []byte("victoria metrics")
sig, _ := priv.Sign(rand.Reader, msg, crypto.Hash(0))
fmt.Println("scheme:  ", mldsa.MLDSA65())
fmt.Println("sig size:", mldsa.MLDSA65().SignatureSize())
fmt.Println("verified:", mldsa.Verify(priv.PublicKey(), msg, sig, nil) == nil)

Also integrated into crypto/x509 and crypto/tls (TLS 1.3).

The UUID Package

The standard library now includes a uuid package (RFC 9562). It generates and parses UUIDs with crypto-random source.

a := uuid.MustParse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
fmt.Println("parsed:", a)
fmt.Println("nil:   ", uuid.Nil())
fmt.Println("max:   ", uuid.Max())

fmt.Println(uuid.NewV4()) // random
fmt.Println(uuid.NewV7()) // time-ordered

uuid.New() picks a suitable algorithm; NewV4() is purely random; NewV7() is time-ordered for database keys.

JSON v2 by Default

The encoding/json/v2 experiment graduates. It's now available without GOEXPERIMENT=jsonv2. The classic encoding/json is now backed by v2 under the hood. Behavior preserved, with new options to pin v1 semantics.

import json "encoding/json/v2"

type Point struct {
    X int `json:"x"`
    Y int `json:"y"`
}

data, _ := json.Marshal(Point{X: 1, Y: 2})
fmt.Println(string(data))

Note: v2 does not sort map keys by default. Use json.Deterministic for stable output.

Portable SIMD

The experimental simd package provides portable SIMD operations. Off by default; enable with GOEXPERIMENT=simd.

a := []float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
b := []float32{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160}
va := simd.LoadFloat32s(a)
vb := simd.LoadFloat32s(b)
sum := va.Add(vb)
out := make([]float32, sum.Len())
sum.Store(out)
fmt.Println(out[:4])

Vector width varies by hardware.

Cut Around the Last Separator

strings.Cut splits on first separator. Go 1.27 adds a new function to split on the last one (name not fully shown, but likely strings.CutLast).

Why This Matters

Generic methods finally land, making generic types more ergonomic. UUID and post-quantum crypto are major additions to the standard library. JSON v2 being default is a big deal for performance and correctness.

What You Should Do Now

  1. Try generic methods with your existing generic types.
  2. Adopt uuid for new projects — no more third-party dependency.
  3. Test JSON v2 with your workloads; watch for map key ordering changes.
  4. Experiment with SIMD if you have CPU-bound loops.
  5. Check the official release notes for the full list.

Go 1.27 is still in development, but these features are already in the source. Start testing them in your codebase today.