基本 + 切片 + 映射类型兼容的泛型?

huangapple go评论99阅读模式
英文:

Basic + Slice + Map Type Compatible Generics?

问题

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

目标

创建一个具有灵活返回类型的切片读取函数生成器:

  1. func ValueReader[T <probably something fancy>](i int) func ([]ProtoConvertable) T {
  2. return func (row []ProtoConvertable) T {
  3. return ...
  4. }
  5. }
  6. row := []ProtoConvertable{
  7. &Data[int]{Value: 333},
  8. &ListData{Values: []ProtoConvertable{
  9. &Data[string]{Value: "hello"},
  10. &Data[string]{Value: "world"},
  11. }},
  12. &MapData{Values: map[ProtoConvertable]ProtoConvertable{
  13. &Data[int]{Value: 22}: &Data[string]{Value: "world"},
  14. &Data[int]{Value: 11}: &Data[string]{Value: "hello"},
  15. }},
  16. }
  17. dataReader := ValueReader[int](0) // 将第一个元素转换为int的函数
  18. listDataReader := ValueReader[[]string](1) // 将第二个元素转换为切片的函数
  19. mapDataReader := ValueReader[map[int]string](2) // 将第三个元素转换为映射的函数
  20. data := dataReader(row) // 333
  21. listData := listDataReader(row) // []string{"hello", "world"}
  22. mapData := mapDataReader(row) // map[int]string{11: "hello", 22: "world"}

类型

  1. type ValueType interface {
  2. int | string
  3. }
  4. type ProtoConvertable interface {
  5. ToProto() *pb.GenericMessage
  6. }
  7. type Data[T ValueType] struct {
  8. Value T
  9. }
  10. func (d *Data) ToProto() *pb.GenericMessage{
  11. ...
  12. }
  13. type ListData struct {
  14. Values []ProtoConvertable
  15. }
  16. func (d *ListData) ToProto() *pb.GenericMessage {
  17. ...
  18. }
  19. type MapData struct {
  20. Values map[ProtoConvertable]ProtoConvertable
  21. }
  22. func (d *MapData) ToProto() *pb.GenericMessage {
  23. ...
  24. }

当前解决方案

  1. func ValueReader[T ValueType](i int) func([]ProtoConvertable) T {
  2. return func(row []ProtoConvertable) T {
  3. return row[i].(*Data[T]).Value
  4. }
  5. }
  6. func ListValueReader[T ValueType](i int) func([]ProtoConvertable) []T {
  7. return func(row []ProtoConvertable) []T {
  8. vs := row[i].(*ListData).Values
  9. res := make([]T, len(vs))
  10. for i, v := range vs {
  11. res[i] = v.(*Data[T]).Value
  12. }
  13. return res
  14. }
  15. }
  16. func MapValueReader[K ValueType, V ValueType](i int) func([]ProtoConvertable) map[K]V {
  17. return func(row []ProtoConvertable) map[K]V {
  18. vs := row[i].(*MapData).Values
  19. res := make(map[K]V, len(vs))
  20. for k, v := range vs {
  21. res[k.(*Data[K]).Value] = v.(*Data[V]).Value
  22. }
  23. return res
  24. }
  25. }
  26. dataReader := ValueReader[int](0)
  27. listDataReader := ListValueReader[string](1)
  28. mapDataReader := MapValueReader[int, string](2)

**注意:**所有这些代码都是一个未经测试的更复杂库的简化版本。可能需要一些调整才能正常工作。

英文:

Is there a way to create a generic function that can adjust its operation when passed a map or a slice type vs a basic type?

Goal

Create a slice reading function generator with a flexible return type:

  1. func ValueReader[T <probably something fancy>](i int) func ([]ProtoConvertable) T {
  2. return func (row []ProtoConvertable) T {
  3. return ...
  4. }
  5. }
  6. row := []ProtoConvertable{
  7. &Data[int]{Value: 333},
  8. &ListData{Values: []ProtoConvertable{
  9. &Data[string]{Value: "hello"},
  10. &Data[string]{Value: "world"},
  11. }},
  12. &MapData{Values: map[ProtoConvertable]ProtoConvertable{
  13. &Data[int]{Value: 22}: &Data[string]{Value: "world"},
  14. &Data[int]{Value: 11}: &Data[string]{Value: "hello"},
  15. }},
  16. }
  17. dataReader := ValueReader[int](0) // A function that converts the first element to an int
  18. listDataReader := ValueReader[[]string](1) // A function that converts the second element to a slice
  19. mapDataReader := ValueReader[map[int]string](2) // A function that converts the third element to a map
  20. data := dataReader(row) // 333
  21. listData := listDataReader(row) // []string{"hello", "world"}
  22. mapData := mapDataReader(row) // map[int]string{11: "hello", 22: "world"}

Types

  1. type ValueType interface {
  2. int | string
  3. }
  4. type ProtoConvertable interface {
  5. ToProto() *pb.GenericMessage
  6. }
  7. type Data[T ValueType] struct {
  8. Value T
  9. }
  10. func (d *Data) ToProto() *pb.GenericMessage{
  11. ...
  12. }
  13. type ListData struct {
  14. Values []ProtoConvertable
  15. }
  16. func (d *ListData) ToProto() *pb.GenericMessage {
  17. ...
  18. }
  19. type MapData struct {
  20. Values map[ProtoConvertable]ProtoConvertable
  21. }
  22. func (d *MapData) ToProto() *pb.GenericMessage {
  23. ...
  24. }

Current Solution

  1. func ValueReader[T ValueType](i int) func([]ProtoConvertable) T {
  2. return func(row []ProtoConvertable) T {
  3. return row[i].(*Data[T]).Value
  4. }
  5. }
  6. func ListValueReader[T ValueType](i int) func([]ProtoConvertable) []T {
  7. return func(row []ProtoConvertable) []T {
  8. vs := row[i].(*ListData).Values
  9. res := make([]T, len(vs))
  10. for i, v := range vs {
  11. res[i] = v.(*Data[T]).Value
  12. }
  13. return res
  14. }
  15. }
  16. func MapValueReader[K ValueType, V ValueType](i int) func([]ProtoConvertable) map[K]V {
  17. return func(row []ProtoConvertable) map[K]V {
  18. vs := row[i].(*MapData).Values
  19. res := make(map[K]V, len(vs))
  20. for k, v := range vs {
  21. res[k.(*Data[K]).Value] = v.(*Data[V]).Value
  22. }
  23. return res
  24. }
  25. }
  26. dataReader := ValueReader[int](0)
  27. listDataReader := ListValueReader[string](1)
  28. mapDataReader := MapValueReader[int, string](2)

Note: all of this code is an untested simplification of a more complicated library. It might need some tweaking to get to actually work.

答案1

得分: 0

"<probably something fancy>"不存在。

主要问题是你想建模一个类型参数,该参数匹配一个基本值和两个复合类型,其中一个是你想捕获KV的映射类型。

即使它存在,ValueReader的主体也将是对T进行类型切换,以返回每个专门的读取器函数,因此你现有的涉及少量代码重复的解决方案似乎在整体上更好。

我的建议是,当T的不同具体类型上的操作确实完全相同时,使用泛型。你可以在这里阅读更多信息:https://go.dev/blog/when-generics

英文:

The <probably something fancy> doesn't exist.

The main issue is that you want to model a type parameter that matches a base value and two composite types, one of which is a map type where you want to capture both K and V.

Even if it existed, the body of ValueReader would be a type-switch on T to return each specialized reader function, so your existing solution that involves a small amount of code duplication seems just a better strategy overall.

My advice is to use generics when the operations on the different concrete types of T are really identical. You can read more at: https://go.dev/blog/when-generics

huangapple
  • 本文由 发表于 2022年4月27日 02:25:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/72018806.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定