Golang中泛型类型的探讨

Golang中泛型类型的探讨 大家好

我正在尝试使用新的泛型方法编写一个用于Option类型的库,我认为这是使用泛型的一个有效场景。

我希望能够在模型中使用这些数据类型,因此需要实现Scan和Value方法。以下是我为结构体编写的代码:

type Option[T any] struct {
	value  T
	isSome bool
}

这是我为Scan方法设计的实现。目前只实现了int64,但当然我希望支持更多的数据类型。

func (o *Option[T]) Scan(value any) error {
	if reflect.TypeOf(value) == nil {
		*o = Option[T]{}
		return nil
	}
	switch reflect.TypeOf(value) {
	case reflect.TypeOf(int64(0)):
		var i sql.NullInt64
		if err := i.Scan(value); err != nil {
			return err
		}
		if i.Valid {
			*o = Option[int64]{
				value:  int64(i.Int64),
				isSome: true,
			}
		} else {
			*o = Option[T]{}
		}
	}

	return nil
}

这段代码无法编译,因为我无法将Option[int64]赋值给o,这我能理解。但是,我该如何编写这样的泛型函数呢?


更多关于Golang中泛型类型的探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

你能展示一个你打算如何使用这个功能的例子吗?

更多关于Golang中泛型类型的探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


是的,这里有一个示例

sentTime := Option[time.Time]{
}

db.QueryRow("SELECT sent_time from mail_sender").Scan(&sentTime)

if (sentTime.IsNone(){
    //Send mail
}

你好 @Michael_Hugi

泛型函数背后的理念是,它能够对参数执行统一的操作,而无需知道具体的类型。如果函数体需要深入探究参数的实际类型,那么这个函数就不能再被认为是泛型的了。因此,我认为类型参数无法帮助你实现你想要的功能。在运行时处理未知类型通常是空接口和反射的领域。

(但如果能证明我是错的,我会很高兴。我对泛型的了解还不够深入,并且确信还有一些泛型结构有待发现。)

这是一个典型的泛型类型约束问题。你需要为泛型类型添加类型约束,确保类型 T 能够支持你需要的操作。以下是修改后的实现:

import (
    "database/sql"
    "database/sql/driver"
    "reflect"
)

// 定义支持的类型约束
type Scannable interface {
    ~int | ~int64 | ~string | ~bool | ~float64
}

type Option[T Scannable] struct {
    value  T
    isSome bool
}

func (o *Option[T]) Scan(value any) error {
    if value == nil {
        *o = Option[T]{}
        return nil
    }

    // 使用类型断言而不是反射
    switch v := value.(type) {
    case int64:
        *o = Option[T]{
            value:  T(v),  // 类型转换
            isSome: true,
        }
    case string:
        *o = Option[T]{
            value:  T(v),
            isSome: true,
        }
    case bool:
        *o = Option[T]{
            value:  T(v),
            isSome: true,
        }
    case float64:
        *o = Option[T]{
            value:  T(v),
            isSome: true,
        }
    default:
        // 处理 sql.Null 类型
        switch val := value.(type) {
        case sql.NullInt64:
            if val.Valid {
                *o = Option[T]{
                    value:  T(val.Int64),
                    isSome: true,
                }
            } else {
                *o = Option[T]{}
            }
        case sql.NullString:
            if val.Valid {
                *o = Option[T]{
                    value:  T(val.String),
                    isSome: true,
                }
            } else {
                *o = Option[T]{}
            }
        default:
            return &sql.ScanError{}
        }
    }
    return nil
}

// Value 方法的实现
func (o Option[T]) Value() (driver.Value, error) {
    if !o.isSome {
        return nil, nil
    }
    return o.value, nil
}

如果你需要支持更多类型,可以扩展 Scannable 约束:

type Scannable interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
    ~float32 | ~float64 |
    ~string | ~bool
}

// 使用示例
func main() {
    var optInt Option[int64]
    optInt.Scan(int64(42))
    
    var optStr Option[string]
    optStr.Scan("hello")
    
    var optBool Option[bool]
    optBool.Scan(true)
}

对于数据库操作,你可能还需要实现 driver.Valuer 接口:

func (o Option[T]) Value() (driver.Value, error) {
    if !o.isSome {
        return nil, nil
    }
    return o.value, nil
}

这样你的 Option 类型就可以在数据库操作中正确使用了。

回到顶部