Golang何时会支持泛型规范?
Golang何时会支持泛型规范? C++和Rust都支持泛型规范,但为什么Go不支持呢?
泛型类型断言的情况如何?
func FormatNumber[T Number](t T, f byte, prec int) (s string) {
defer func() { recover() }()
switch v := reflect.ValueOf(t); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64:
s = strconv.FormatInt(v.Int(), f, prec)
case reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
s = strconv.FormatUint(v.Uint(), f, prec)
case reflect.Float32:
s = strconv.FormatFloat(v.Float(), f, prec, 32)
case reflect.Float64:
s = strconv.FormatFloat(v.Float(), f, prec, 64)
case reflect.Complex64:
s = strconv.FormatComplex(v.Complex(), f, prec, 64)
case reflect.Complex128:
s = strconv.FormatComplex(v.Complex(), f, prec, 128)
default:
s = v.String()
}
if f >= 'A' && f <= 'Z' {
s = ToUpper(s)
}
return s
}
变为
func FormatNumber[T Number](t T, f byte, prec int) (s string) {
switch v := t.(type) {
case ~int, ~int8, ~int16, ~int32, ~int64:
s = strconv.FormatInt(int64(v), f, prec)
case ~uint, ~uint8, ~uint16, ~uint32, ~uint64, ~uintptr:
s = strconv.FormatUint(uint64(v), f, prec)
case ~float32:
s = strconv.FormatFloat(float64(v), f, prec, 32)
case ~float64:
s = strconv.FormatFloat(float64(v), f, prec, 64)
case ~complex64:
s = strconv.FormatComplex(complex128(v), f, prec, 64)
case ~complex128:
s = strconv.FormatComplex(complex128(v), f, prec, 128)
}
if f >= 'A' && f <= 'Z' {
s = ToUpper(s)
}
return s
}
更多关于Golang何时会支持泛型规范?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我们希望对相同基础类型的命名类型进行分组,这样在添加新的命名数值类型时,就无需重写 switch 语句。
更多关于Golang何时会支持泛型规范?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我认为你可以将代码从类型切换改为使用带有显式类型断言的 switch 语句,以便将它们分组处理。vUint, ok := v.(uint64) 对于所有可以转换为 uint64 的类型,ok 都会为 true 并返回相应的值。
func main() {
fmt.Println("hello world")
}
有一种方法可以在不使用反射的情况下实现。但这会导致对可能类型的处理更加明确,请在 playground 上查看:
import (
"fmt"
"strconv"
"strings"
"golang.org/x/exp/constraints"
)
type Number interface {
constraints.Integer | constraints.Complex | constraints.Float
}
func FormatNumber[T Number](t T, f byte, prec int) (s string) {
switch v := any(t).(type) {
case int:
s = strconv.FormatInt(int64(v), 10)
case int8:
s = strconv.FormatInt(int64(v), 10)
case int16:
s = strconv.FormatInt(int64(v), 10)
case int32:
s = strconv.FormatInt(int64(v), 10)
case int64:
s = strconv.FormatInt(v, 10)
case uint:
s = strconv.FormatUint(uint64(v), 10)
case uint8:
s = strconv.FormatUint(uint64(v), 10)
case uint16:
s = strconv.FormatUint(uint64(v), 10)
case uint32:
s = strconv.FormatUint(uint64(v), 10)
case uint64:
s = strconv.FormatUint(v, 10)
case float32:
s = strconv.FormatFloat(float64(v), f, prec, 32)
case float64:
s = strconv.FormatFloat(v, f, prec, 64)
case complex64:
s = strconv.FormatComplex(complex128(v), f, prec, 64)
case complex128:
s = strconv.FormatComplex(v, f, prec, 128)
default:
panic("unsupported number type")
}
if f >= 'A' && f <= 'Z' {
s = strings.ToUpper(s)
}
return s
}
无需在添加新的命名数值类型时重写 switch 语句
你能详细说明一下“添加新的命名数值类型”吗?比如你的项目正在实现新的数值类型?如果是这样,你将控制它们,并且你总是可以实现 Stringer 接口:
type NewNumber int64
func (n NewNumber) String() string {
return "the number"
}
//... 然后在你的 switch 中
default:
s = fmt.Sprint(v) // 默认使用 stringer 接口
如果你想要更多控制,你可以直接定义并实现你自己的接口:
type NewNumber int64
type Formatter interface {
Format(base int) string
}
func (n NewNumber) Format(base int) string {
return strconv.FormatInt(int64(n), base)
}
// ... 稍后在你的 switch 语句中
case Formatter:
s = v.Format(prec)
无论如何,我认为目前没有直接的方法可以做你想做的事情。请参阅这个议题及其最终评论:
类型 switch 不支持多 case 吗?
你使用的是哪个版本的 Go (
go version)?$ go version 1.13.3这个问题在最新版本中是否仍然存在?
是的
你使用的操作系统和处理器架构是什么 (
go env)?`go env` 输出
$ go env GOHOSTARCH="amd64" GOHOSTOS="linux" GOOS="linux" GOROOT="/usr/lib/golang"我忽略了一些不相关的信息。
你做了什么?
// ConvertFloat64 convert any type to float64 if it can. func ConvertFloat64(v interface{}) (ret float64, err error) { switch val := v.(type) { case float64: return val, nil case float32: return float64(val), nil case uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64: return float64(val), nil default: err = fmt.Errorf("unsupported value to convert to float64,%v", v) } return }当我在类型 switch 中使用多 case 时,会发生错误:
cannot convert val (type interface {}) to type float64: need type assertion第 9 行。当我切换到单个 case 时,错误不会发生。 我想知道为什么我不能合并这些 case 分支。
谢谢。
你期望看到什么?
你实际看到了什么?
Go 1.18 已经正式支持泛型。你提供的代码示例展示了在泛型函数中进行类型断言的理想写法,但目前Go的泛型实现还不支持这种直接在泛型类型参数上使用 switch v := t.(type) 的语法。
当前Go泛型的类型约束可以通过接口定义,但类型开关(type switch)只能作用于接口类型,不能直接作用于类型参数。以下是当前可用的实现方式:
type Number interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~complex64 | ~complex128
}
func FormatNumber[T Number](t T, f byte, prec int) string {
var s string
switch any(t).(type) {
case int:
s = strconv.FormatInt(int64(t), 10)
case int8:
s = strconv.FormatInt(int64(t), 10)
case int16:
s = strconv.FormatInt(int64(t), 10)
case int32:
s = strconv.FormatInt(int64(t), 10)
case int64:
s = strconv.FormatInt(int64(t), 10)
case uint:
s = strconv.FormatUint(uint64(t), 10)
case uint8:
s = strconv.FormatUint(uint64(t), 10)
case uint16:
s = strconv.FormatUint(uint64(t), 10)
case uint32:
s = strconv.FormatUint(uint64(t), 10)
case uint64:
s = strconv.FormatUint(uint64(t), 10)
case float32:
s = strconv.FormatFloat(float64(t), f, prec, 32)
case float64:
s = strconv.FormatFloat(float64(t), f, prec, 64)
case complex64:
s = strconv.FormatComplex(complex128(t), f, prec, 64)
case complex128:
s = strconv.FormatComplex(complex128(t), f, prec, 128)
}
if f >= 'A' && f <= 'Z' {
s = strings.ToUpper(s)
}
return s
}
或者使用类型断言:
func FormatNumber[T Number](t T, f byte, prec int) string {
var s string
switch v := any(t).(type) {
case int:
s = strconv.FormatInt(int64(v), 10)
case int8:
s = strconv.FormatInt(int64(v), 10)
// ... 其他类型类似
}
return s
}
Go团队在泛型设计中选择了保守的实现方案,目前不支持在类型参数上直接使用类型开关。这种设计决策是为了保持语言简单性和编译效率。虽然不能直接使用你期望的语法,但通过 any(t).(type) 转换可以达到类似效果。

