为了账号安全,请及时绑定邮箱和手机立即绑定

如何分配或返回受联合约束的通用 T?

如何分配或返回受联合约束的通用 T?

Go
侃侃尔雅 2022-11-08 14:37:46
换句话说,我如何为联合类型集中的不同类型实现特定于类型的解决方案?鉴于以下代码...type FieldType interface {    string | int}type Field[T FieldType] struct {    name         string    defaultValue T}func NewField[T FieldType](name string, defaultValue T) *Field[T] {    return &Field[T]{        name:         name,        defaultValue: defaultValue,    }}func (f *Field[T]) Name() string {    return f.name}func (f *Field[T]) Get() (T, error) {    value, ok := os.LookupEnv(f.name)    if !ok {        return f.defaultValue, nil    }    return value, nil}编译器显示错误:field.go:37:9: cannot use value (variable of type string) as type T in return statement有没有办法为所有可能FieldType的 s 提供实现?喜欢...func (f *Field[string]) Get() (string, error) {    value, ok := os.LookupEnv(f.name)    if !ok {        return f.defaultValue, nil    }    return value, nil}func (f *Field[int]) Get() (int, error) {    raw, ok := os.LookupEnv(f.name)    if !ok {        return f.defaultValue, nil    }    value, err := strconv.ParseInt(raw, 10, 64)    if err != nil {        return *new(T), err    }    return int(value), nil}欢迎任何提示。
查看完整描述

2 回答

?
天涯尽头无女友

TA贡献1831条经验 获得超9个赞

发生错误是因为涉及类型参数的操作(包括赋值和返回)必须对其类型集中的所有类型都有效。在 的情况下string | int,没有从字符串初始化它们的值的通用操作。


但是,您仍然有几个选择:


类型切换T

您在类型切换中使用具有泛型类型的字段T,并将具有具体类型的值临时设置为interface{}/ any。然后类型断言接口返回T以返回它。请注意,此断言是未经检查的,因此如果出于某种原因ret持有不在T. 当然你可以用 comma-ok 检查它,但它仍然是一个运行时断言:


func (f *Field[T]) Get() (T, error) {

    value, ok := os.LookupEnv(f.name)

    if !ok {

        return f.defaultValue, nil

    }

    var ret any

    switch any(f.defaultValue).(type) {

    case string:

        ret = value


    case int:

        // don't actually ignore errors

        i, _ := strconv.ParseInt(value, 10, 64)

        ret = int(i)

    }

    return ret.(T), nil

}

类型切换*T

您可以进一步简化上面的代码并摆脱空接口。在这种情况下,您获取T-type 变量的地址并打开指针类型。这是在编译时完全类型检查的:


func (f *Field[T]) Get() (T, error) {

    value, ok := env[f.name]

    if !ok {

        return f.defaultValue, nil

    }


    var ret T

    switch p := any(&ret).(type) {

    case *string:

        *p = value


    case *int:

        i, _ := strconv.ParseInt(value, 10, 64)

        *p = int(i)

    }

    // ret has the zero value if no case matches

    return ret, nil

}

请注意,在这两种情况下,您都必须将T值转换为interface{}/any才能在类型开关中使用它。您不能直接在T.


带地图模拟的游乐场os.LookupEnv:https ://go.dev/play/p/LHqizyNL9lP


查看完整回答
反对 回复 2022-11-08
?
饮歌长啸

TA贡献1951条经验 获得超3个赞

好的,如果使用反射,类型开关可以工作。


func (f *Field[T]) Get() (T, error) {

    raw, ok := os.LookupEnv(f.name)

    if !ok {

        return f.defaultValue, nil

    }


    v := reflect.ValueOf(new(T))


    switch v.Type().Elem().Kind() {

    case reflect.String:

        v.Elem().Set(reflect.ValueOf(raw))


    case reflect.Int:

        value, err := strconv.ParseInt(raw, 10, 64)

        if err != nil {

            return f.defaultValue, err

        }

        v.Elem().Set(reflect.ValueOf(int(value)))

    }


    return v.Elem().Interface().(T), nil

}

但是非常欢迎更好的解决方案;-)


查看完整回答
反对 回复 2022-11-08
  • 2 回答
  • 0 关注
  • 84 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信