Mastering Go 1.24 Weak References: Using the New std‑lib weak Package

This article explains Go 1.24's new std‑lib weak package, compares it with weak reference implementations in other languages, demonstrates its API with practical code examples, and shows how to build canonicalization maps and fixed‑size caches using weak pointers.

Radish, Keep Going!
Radish, Keep Going!
Radish, Keep Going!
Mastering Go 1.24 Weak References: Using the New std‑lib weak Package

What is weak?

Go 1.24 adds a new standard‑library package weak that lets you create safe references ( *T) without preventing the referenced object from being reclaimed by the garbage collector.

Package weak provides ways to safely reference memory weakly, that is, without preventing its reclamation.

Like OS.ROOT, weak has existed in many other languages: Java: WeakReference and SoftReference for caches and object pools, reclaimed when the JVM is low on memory. Python: the weakref module for avoiding circular references and caching. C++: std::shared_ptr with std::weak_ptr to break reference cycles. Rust: Rc / Arc provide Weak references for similar purposes.

The weak API is simple: a Make method returns a weak.Pointer, and a Value method retrieves the original T or nil if it has been collected.

weak pointer diagram
weak pointer diagram

Example:

func main() {
    originalObject := "Hello, World!"
    runtime.AddCleanup(&originalObject, func(s int64) {
        fmt.Println("originalObject clean at: ", s)
    }, time.Now().Unix())
    weakPtr := weak.Make(&originalObject)
    fmt.Println(fmt.Sprintf("originalObject:addr %x", &originalObject))
    fmt.Println(fmt.Sprintf("weakPtr addr:%x,size:%d", weakPtr, unsafe.Sizeof(weakPtr)))
    runtime.GC()
    time.Sleep(1 * time.Millisecond)
    value := weakPtr.Value()
    if value != nil && strings.Contains(*value, originalObject) {
        fmt.Println("First GC :value: ", *value)
    } else {
        fmt.Println("first gc. Weak reference value is nil")
    }
    runtime.GC()
    time.Sleep(1 * time.Millisecond)
    value = weakPtr.Value()
    if value != nil {
        fmt.Println("Second GC", *value)
    } else {
        fmt.Println("Second GC: Weak reference value is nil")
    }
}

Running this program shows that after the first GC the weak reference still returns the string, but after the second GC it returns nil because the original object is no longer reachable.

The example also uses runtime.AddCleanup, a new Go 1.24 feature similar to runtime.SetFinalizer, which runs a callback when an object is collected. weak.Make creates an intermediate address ( weak.Printer) that hides the real pointer. weak.Printer does not prevent the real object from being reclaimed; if it is reclaimed, weak.Printer.Value returns nil. Therefore callers must always check the return value.

What can weak be used for?

Canonicalization maps

In Go 1.23 the unique feature deduplicated identical strings by storing a single pointer. Go 1.24 rewrites unique using weak, achieving a similar memory‑saving effect.

Fixed‑size cache

Below is a sketch of a fixed‑size cache that combines weak with list.List:

type WeakCache struct {
    cache   map[string]weak.Pointer[list.Element] // weak references to values
    mu      sync.Mutex
    storage Storage
}

type Storage struct {
    capacity int // maximum cache size
    list     *list.List
}

func (c *WeakCache) Set(key string, value any) {
    // Update existing element if it still exists
    if elem, exists := c.cache[key]; exists {
        if elemValue := elem.Value(); elemValue != nil {
            elemValue.Value = &CacheItem{key: key, value: value}
            c.storage.list.MoveToFront(elemValue)
            c.cache[key] = weak.Make(elemValue)
            return
        } else {
            c.removeElement(key)
        }
    }
    // Evict if capacity reached
    if c.storage.list.Len() >= c.storage.capacity {
        c.evict()
    }
    // Add new element
    elem := c.storage.list.PushFront(&CacheItem{key: key, value: value})
    c.cache[key] = weak.Make(elem)
}

The full source is available at https://gist.github.com/hxzhouh/1945d4a1e5a6567f084628d60b63f125 . By storing cache entries as weak pointers, the cache automatically discards items that are no longer referenced elsewhere, enabling an efficient fixed‑size cache without explicit eviction logic.

In summary, Go's new weak package provides a simple API for creating weak references, useful for canonicalization, caches, and other scenarios where you want objects to be reclaimed automatically when no longer needed.

Further reading:

Go 1.24 weak package documentation

Discussion of weak references in Go

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

cacheweak references
Radish, Keep Going!
Written by

Radish, Keep Going!

Personal sharing

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.