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

换句话说,我如何为联合类型集中的不同类型实现特定于类型的解决方案?


鉴于以下代码...


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

}

欢迎任何提示。


侃侃尔雅
浏览 63回答 2
2回答

天涯尽头无女友

发生错误是因为涉及类型参数的操作(包括赋值和返回)必须对其类型集中的所有类型都有效。在 的情况下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

饮歌长啸

好的,如果使用反射,类型开关可以工作。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}但是非常欢迎更好的解决方案;-)
打开App,查看更多内容
随时随地看视频慕课网APP