Search code examples
gogenericsinterface

Instantiate generic interface with any, struct doesn't implement it


Can someone explain, why *DataTo does not satisfy ToType[any]?

Trying to build a DTOer, that copies all values of one struct to another and also sets some explicit values (V in this case)

https://go.dev/play/p/-oobZrw5Ewe

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

type DataFrom struct {
    V1 int
}

type DataTo struct {
    V int
}

func (m *DataTo) SetVal() {
    m.V = 1
    return
}

type ToType[T any] interface {
    SetVal()
    *T
}

type DTO[TFrom any, TTo ToType[any]] struct {
    Get func(from TFrom) TTo
}

func main() {
    dto := &DTO[DataFrom, *DataTo]{
        Get: func(from DataFrom) *DataTo {
            return &DataTo{V: from.V1 + 666}
        },
    }

    vFrom := DataFrom{V1: 1}
    vTo := dto.Get(vFrom)

    fmt.Println(vTo.V)
}

Solution

  • Because any is a static type.

    If you use it to instantiate a generic type like ToType, that generic type will expect exactly any.

    Now, certain usages of the type parameter might hide this issue, for example:

    type Foo[T any] struct {
        Value T
    }
    
    Foo[any]{Value: 12} // ok
    

    Normally you are able to assign whatever type to any like the above, because any is just an alias of the empty interface interface{}, and any type satisfies the empty interface.

    When the type parameter is used in composite types such as *T, then instantiating with any means exactly *any. Therefore you can imagine ToType[any] as the same thing as:

    type ToTypeAny interface {
        SetVal()
        *any
    }
    

    And then *DataTo is obviously not *any. Further details: Assign struct pointer to interface pointer

    If you declare the struct as follows it will compile:

    type DTO[TFrom any, TTo ToType[DataTo]] struct {
        Get func(from TFrom) TTo
    }
    

    Or in a more "generic" but also more verbose way:

    type DTO[TFrom any, T any, TTo ToType[T]] struct {
        Get func(from TFrom) TTo
    }
    
    &DTO[DataFrom, DataTo, *DataTo]{ ... }