Go泛型函数调用报错:不可比较类型问题求助
问题原因
Go中,若结构体包含切片、映射、函数等不可比较类型,整个结构体就失去了可比较性。你的AppleKeyStore结构体里有SharedPasswords切片字段,导致该结构体无法直接用==或!=比较;而泛型约束AppleKeyStore | GoogleKeyStore因包含不可比较类型,类型集整体不可比较,所以saveKeyStore函数里的old != new会触发incomparable types in type set错误。
解决方案
方案1:自定义Equal方法(推荐)
给两个结构体定义统一的比较方法,通过泛型约束强制类型实现该方法,在saveKeyStore中调用方法完成比较。
修改后的完整代码:
package main import ( "encoding/json" "errors" "fmt" "net/http" "net/url" "sync" ) var m sync.Map // 定义泛型约束,要求类型实现Equal方法 type KeyStoreComparer[T any] interface { Equal(other T) bool } type AccountResponse[Payload KeyStoreComparer[Payload]] struct { Data struct { Options Payload `json:"options"` } `json:"data"` Code int `json:"code"` } type AppleKeyStore struct { KeyID string `json:"kid"` SharedPasswords []struct { BundleID string `json:"bundle_id"` Password string `json:"password"` } `json:"shared_passwords"` } // AppleKeyStore实现Equal方法,逐字段对比 func (a AppleKeyStore) Equal(other AppleKeyStore) bool { if a.KeyID != other.KeyID { return false } if len(a.SharedPasswords) != len(other.SharedPasswords) { return false } for i := range a.SharedPasswords { if a.SharedPasswords[i].BundleID != other.SharedPasswords[i].BundleID || a.SharedPasswords[i].Password != other.SharedPasswords[i].Password { return false } } return true } type GoogleKeyStore struct { AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"` AuthUri string `json:"auth_uri"` Bucket string `json:"bucket"` } // GoogleKeyStore实现Equal方法 func (g GoogleKeyStore) Equal(other GoogleKeyStore) bool { return g.AuthProviderX509CertURL == other.AuthProviderX509CertURL && g.AuthUri == other.AuthUri && g.Bucket == other.Bucket } func getCachedKeyStore[Payload KeyStoreComparer[Payload]](appID string) (*Payload, error) { value, ok := m.Load(appID) if ok { t := value.(Payload) return &t, nil } else { return nil, errors.New("cache keystore not exist") } } func saveKeyStore[T KeyStoreComparer[T]](appID string, old, new T) { if !old.Equal(new) { m.Store(appID, new) } } func fetchRemoteAccount[Payload KeyStoreComparer[Payload]](accountID string) (*AccountResponse[Payload], error) { urlStr := "" client := &http.Client{} params := url.Values{} params.Add("id", accountID) req, err := http.NewRequest(http.MethodGet, urlStr, nil) if err != nil { return nil, err } req.URL.RawQuery = params.Encode() req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", "token")) resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("fetch secret api, response status code: %d", resp.StatusCode) } var appleAccountResponse AccountResponse[Payload] err = json.NewDecoder(resp.Body).Decode(&appleAccountResponse) if err != nil { return nil, err } return &appleAccountResponse, nil } func getKeyStoreFromRemoteOrCache[Payload KeyStoreComparer[Payload]](accountID, appID string) (*Payload, error) { remoteAccountRespone, fetchErr := fetchRemoteAccount[Payload](accountID) cachedKeyStore, getCacheErr := getCachedKeyStore[Payload](appID) if fetchErr != nil { if getCacheErr != nil { return nil, errors.New("fetch account failed and get cache apple account failed") } else { return cachedKeyStore, nil } } else { // 处理缓存不存在的情况:直接存储新值 if getCacheErr != nil { m.Store(appID, remoteAccountRespone.Data.Options) } else { saveKeyStore(appID, *cachedKeyStore, remoteAccountRespone.Data.Options) } return &remoteAccountRespone.Data.Options, nil } } func main() { getKeyStoreFromRemoteOrCache[GoogleKeyStore]("", "") }
方案2:序列化后比较
将结构体序列化为JSON字节串,比较字节串是否相同。这种方法无需为每个结构体写Equal方法,但性能略低,且依赖JSON序列化的一致性。
修改saveKeyStore函数如下:
import "encoding/json" func saveKeyStore[T AppleKeyStore | GoogleKeyStore](appID string, old, new T) { oldBytes, _ := json.Marshal(old) newBytes, _ := json.Marshal(new) if string(oldBytes) != string(newBytes) { m.Store(appID, new) } }
关键修正点
原代码中getKeyStoreFromRemoteOrCache函数存在空指针 panic 隐患:当缓存不存在时,*cachedKeyStore会触发崩溃,方案1中已添加缓存不存在的判断逻辑。
内容的提问来源于stack exchange,提问作者bing_2019
相关产品推荐
相关产品推荐

