Basic + Slice + Map 类型兼容泛型?

Basic + Slice + Map Type Compatible Generics?

有没有办法创建一个通用函数,在传递映射或切片类型与基本类型时可以调整其操作?

目标

创建具有灵活return类型的切片读取函数生成器:

func ValueReader[T <probably something fancy>](i int) func ([]ProtoConvertable) T {
    return func (row []ProtoConvertable) T {
        return ...
    }
}

row := []ProtoConvertable{
    &Data[int]{Value: 333},
    &ListData{Values: []ProtoConvertable{
        &Data[string]{Value: "hello"},
        &Data[string]{Value: "world"},
    }},
    &MapData{Values: map[ProtoConvertable]ProtoConvertable{
        &Data[int]{Value: 22}: &Data[string]{Value: "world"},
        &Data[int]{Value: 11}: &Data[string]{Value: "hello"},
    }},
}

dataReader := ValueReader[int](0) // A function that converts the first element to an int
listDataReader := ValueReader[[]string](1) // A function that converts the second element to a slice
mapDataReader := ValueReader[map[int]string](2) // A function that converts the third element to a map

data := dataReader(row) // 333
listData := listDataReader(row) // []string{"hello", "world"}
mapData := mapDataReader(row) // map[int]string{11: "hello", 22: "world"}

类型

type ValueType interface {
    int | string
}

type ProtoConvertable interface {
    ToProto() *pb.GenericMessage
}

type Data[T ValueType] struct {
    Value T
}

func (d *Data) ToProto() *pb.GenericMessage{
    ...
}

type ListData struct {
    Values []ProtoConvertable
}

func (d *ListData) ToProto() *pb.GenericMessage {
    ...
}

type MapData struct {
    Values map[ProtoConvertable]ProtoConvertable
}

func (d *MapData) ToProto() *pb.GenericMessage {
    ...
}

当前解决方案

func ValueReader[T ValueType](i int) func([]ProtoConvertable) T {
    return func(row []ProtoConvertable) T {
        return row[i].(*Data[T]).Value
    }
}

func ListValueReader[T ValueType](i int) func([]ProtoConvertable) []T {
    return func(row []ProtoConvertable) []T {
        vs := row[i].(*ListData).Values
        res := make([]T, len(vs))
        for i, v := range vs {
            res[i] = v.(*Data[T]).Value
        }
        return res
    }
}

func MapValueReader[K ValueType, V ValueType](i int) func([]ProtoConvertable) map[K]V {
    return func(row []ProtoConvertable) map[K]V {
        vs := row[i].(*MapData).Values
        res := make(map[K]V, len(vs))
        for k, v := range vs {
            res[k.(*Data[K]).Value] = v.(*Data[V]).Value
        }
        return res
    }
}

dataReader := ValueReader[int](0)
listDataReader := ListValueReader[string](1)
mapDataReader := MapValueReader[int, string](2)

注意:所有这些代码都是一个更复杂的库的未经测试的简化。它可能需要一些调整才能真正起作用。

<probably something fancy>不存在。

主要问题是您想要对匹配基值 和两个复合类型 的类型参数进行建模,其中一个是您想要捕获两者的映射类型KV.

即使存在,ValueReader 的主体也将是 T 到 return 上的 type-switch 每个专门的 reader 函数,因此您现有的涉及少量代码重复的解决方案总体上似乎只是一个更好的策略。

我的建议是,当对 T 的不同具体类型的操作完全相同时,使用泛型。您可以在以下位置阅读更多信息:https://go.dev/blog/when-generics