Search code examples
gogenericsconstraintstype-inference

cannot infer V: infer type parameter from constraint implementation


I have an interface in go which wants to support saving and loading results in different databases and I want to support different types.

package cfgStorage

type WritableType interface {
    ~int | ~string | ~float64
}

type ConfigStorage[K, V WritableType] interface {
    get(key K) (V, error)
    set(key K, value V) (bool, error)
}

func GetValue[K, V WritableType, C ConfigStorage[K, V]](storage C, key K) (V, error) {
    res, err := storage.get(key)
    return res, err
}

func SetValue[K, V WritableType, C ConfigStorage[K, V]](storage C, key K, value V) (bool, error) {
    res, err := storage.set(key, value)
    return res, err
}

I implemented fileSystem storage for this interface as below:

type FileSystemStorage[K, V WritableType] struct {
}

func (f FileSystemStorage[K, V]) get(key K) (V, error) {
    /// my code to load data from json file
}

func (f FileSystemStorage[K, V]) set(key K, value V) (bool, error) {
/// my code to save data as json file
}

BTW when I try to get an instance from fileSystem and SetValue it works, but for GetValue I faced a compiler error, my test code is as following:

var fileStorage cfgStorage.FileSystemStorage[string, string]

setResult, _ := cfgStorage.SetValue(fileStorage, "key", "value")
if setResult == false {
    t.Error()
}
var result string

result, _ = cfgStorage.GetValue(fileStorage, "key")

The compile error is in the line where I called GetValue:

cannot infer V

If you have any idea how to solve this issue please let me know!


Solution

  • Go 1.21

    This program now works without specifying the type constraints, because type inference now considers the types used in interface methods. From the release notes:

    Type inference now also considers methods when a value is assigned to an interface: type arguments for type parameters used in method signatures may be inferred from the corresponding parameter types of matching methods.

    Therefore you can simply call:

    result, _ = GetValue(fileStorage, "key")
    

    Playground: https://go.dev/play/p/XBuOTREHd5Y

    Go 1.20 and lower

    In the function GetValue, it's not possible to infer the type of V with only the provided arguments storage C and key K.

    You are asking to infer V from the concrete type that implements the generic constraint ConfigStorage[K, V]. The current type inference algorithm doesn't support this. Related issues in the Go github repository are 41176: cannot infer generic interface types , as well as 50484 and 40018.

    Also relevant proposal section about type inference:

    We can use function argument type inference for a function call to deduce type arguments from the types of the non-type arguments. We can use constraint type inference to deduce unknown type arguments from known type arguments.

    So you could argue that C is not actually known, you only know that it implements the constraint ConfigStorage[K, V].

    You must call GetValue with explicit type parameters:

    // first string for K, second string for V
    GetValue[string, string](fileStorage, "key")
    

    Fixed playground: https://gotipplay.golang.org/p/KoYZ3JMEz2N