Singleton pattern with Go generics

WBOY
Release: 2024-02-05 22:54:07
forward
765 people have browsed it

带有 Go 泛型的单例模式

Question content

I'm trying to figure out the least worst way to implement a singleton for a generic variable in golang. Using the plain sync.once pattern with global variables won't work because the common type information isn't available there (below).

This example is contrived, but in practice, the code that maintains the singleton can be separated from the client code that defines t (e.g. in a library).

Assume this library code, where the specific value of t is unknown:

type cache[t any] struct{}

var (
    cacheonce sync.once
    cache     cache[any] // global singleton
)

func getorcreatecache[t any]() cache[t] {
    cacheonce.do(func() {
        typedcache := buildcache()
        cache = typedcache.(cache[any]) // invalid type assertion
    })
    return cache.(cache[t]) // invalid type assertion
}
Copy after login

And assume this separate client code where t is defined as string:

stringCache := getOrCreateCache[string]()
Copy after login

What is the best way to achieve this?


Correct answer


I ended up solving this problem using the atomic.pointer api and integrated it into a simple library for Anyone else interested use: One-line. Reprocessing the original post using singlets looks like this:

Sample library code:

type cache[t any] struct{}

var singleton = &singlet.singleton{}

func getorcreatecache[t any]() (cache[t], err) {
    return singlet.getordo(singleton, func() cache[t] {
        return cache[t]{}
    })
}
Copy after login

Client code:

stringcache := getorcreatecache[string]()
Copy after login

And the singlet library code that supports this feature:

var ErrTypeMismatch = errors.New("the requested type does not match the singleton type")

type Singleton struct {
    p   atomic.Pointer[any]
    mtx sync.Mutex
}

func GetOrDo[T any](singleton *Singleton, fn func() T) (result T, err error) {
    maybeResult := singleton.p.Load()
    if maybeResult == nil {
        // Lock to guard against applying fn twice
        singleton.mtx.Lock()
        defer singleton.mtx.Unlock()
        maybeResult = singleton.p.Load()

        // Double check
        if maybeResult == nil {
            result = fn()
            var resultAny any = result
            singleton.p.Store(&resultAny)
            return result, nil
        }
    }

    var ok bool
    result, ok = (*maybeResult).(T)
    if !ok {
        return *new(T), ErrTypeMismatch
    }
    return result, nil
}
Copy after login

I hope this helps others who encounter this situation.

The above is the detailed content of Singleton pattern with Go generics. For more information, please follow other related articles on the PHP Chinese website!

source:stackoverflow.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template