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

Phi*_*üll 12 generics go type-switch

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

鉴于以下代码...

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
}
Run Code Online (Sandbox Code Playgroud)

编译器显示错误:

field.go:37:9: cannot use value (variable of type string) as type T in return statement
Run Code Online (Sandbox Code Playgroud)

有没有办法为所有可能的 s 提供实现FieldType

喜欢...

field.go:37:9: cannot use value (variable of type string) as type T in return statement
Run Code Online (Sandbox Code Playgroud)

任何提示都会受到欢迎。

bla*_*een 27

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

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

类型开关打开T

T您可以在类型开关中使用具有泛型类型的字段,并将具有具体类型的值临时设置到interface{}/中any。然后键入断言接口返回T以返回它。请注意,此断言未经检查,因此如果由于某种原因ret持有不属于 类型集中的内容,它可能会出现恐慌T。当然你可以用逗号-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
}
Run Code Online (Sandbox Code Playgroud)

类型开关打开*T

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

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
}
Run Code Online (Sandbox Code Playgroud)

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

带有模拟地图的游乐场os.LookupEnvhttps://go.dev/play/p/JVBEZwCXRMW