type futureCache … // newFutureCache returns a futureCache that is ready to coordinate // computations via [futureCache.get]. // // If persistent is true, the results of these computations are stored for the // lifecycle of cache. Otherwise, results are discarded after they have been // passed to all awaiting goroutines. func newFutureCache[K comparable, V any](persistent bool) *futureCache[K, V] { … } type future … type cacheFunc … // get retrieves or computes the value corresponding to k. // // If the cache if persistent and the value has already been computed, get // returns the result of the previous computation. Otherwise, get either starts // a computation or joins an ongoing computation. If that computation is // cancelled, get will reassign the computation to a new goroutine as long as // there are awaiters. // // Once the computation completes, the result is passed to all awaiting // goroutines. If the cache is transient (persistent=false), the corresponding // cache entry is removed, and the next call to get will execute a new // computation. // // It is therefore the responsibility of the caller to ensure that the given // compute function is safely retryable, and always returns the same value. func (c *futureCache[K, V]) get(ctx context.Context, k K, compute cacheFunc[V]) (V, error) { … }