gogenerics

Difference between map of interface and map of generic value constrained to that interface


Is there a difference between Environment and Environment_g in the following example?

package main

import "fmt"

type Variable[A any] interface {
    Get() A
    Set(A) error
}

type Environment map[string]Variable[any]
type Environment_g[V Variable[any]] map[string]V

func main() {
    e := new(Environment)
    eg := e
    fmt.Println(e, eg)
}

Or are this just two ways to do the same?


Solution

  • There is no difference in the sense that given a type that implements the interface Variable[any], you can assign it to the values of both maps:

    type Foo struct {
        v any
    }
    
    func (f Foo) Get() any {
        return f.v
    }
    
    func (f Foo) Set(v any) error {
        f.v = v
        return nil
    }
    

    You can assign it to both maps:

    func main() {
        e := Environment{}
        g := Environment_g[Foo]{}
    
        e["key"] = Foo{1}
        g["key"] = Foo{2}
    }
    

    The second definition just adds a layer of abstraction (obfuscation?).

    The most glaring difference is:

    This is the same as map[string]any vs. map[string]int32: the former can hold int32 values among others, whereas the latter obviously can't.

    Another closely related reading is: Difference between any/interface{} as constraint vs. type of argument?

    Other than that, they are not the same type, just like map[string]any and map[string]int32 are not, with all the usual implications: you can't convert one type to the other, can't assign one type to the other, etc.

    Again, when thinking about this consider what the generic type is equivalent to after instantiation. It eventually boils down to map[string]someInterface and map[string]concreteType.

    The only case where the maps would be almost equivalent is when you instantiate the type parameter with the same value type of the non-generic map. But the types still have different names, so a conversion is required.

    a := Environment{} // it's a map[string]Variable[any]
    b := Environment_g[Variable[any]] // it's also a map[string]Variable[any]
    
    a = Environment(b) // ok, with type conversion
    

    This answer ignores the red herring of instantiating a type parameter with any. For that see this