如何优雅比较Go泛型数字类型是否大于零?
泛型正数校验函数的优雅优化方案
我有一个用于校验正数的Positive泛型函数,当前实现可正常工作但代码冗余不够优雅。因兼容性问题需沿用[]error参数而非返回错误。尝试合并类型分支时出现**interface conversion: interface {} is int, not int64**的panic,请问有什么更优的方式来比较泛型数字是否大于零?
原实现代码
type Positiver interface { decimal.Decimal | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64 } //nolint:cyclop func Positive[T Positiver](value T, name string, errs *[]error) { addError := func() { err := fmt.Errorf(`%s %w, but it's %v`, name, failures.ShouldBePositive, value) *errs = append(*errs, err) } const prescision = 8 switch val := any(value).(type) { case decimal.Decimal: if val.IsNegative() || val.IsZero() { err := fmt.Errorf(`%s %w, but it's %s`, name, failures.ShouldBePositive, val.StringFixedBank(prescision)) *errs = append(*errs, err) } return case int: if val <= 0 { addError() } case int64: if val <= 0 { addError() } case int32: if val <= 0 { addError() } case int16: if val <= 0 { addError() } case int8: if val <= 0 { addError() } case uint: if val <= 0 { addError() } case uint64: if val <= 0 { addError() } case uint32: if val <= 0 { addError() } case uint16: if val <= 0 { addError() } case uint8: if val <= 0 { addError() } case float32: if val <= 0 { addError() } case float64: if val <= 0 { addError() } default: panic(fmt.Sprintf(`%T is not supported type`, val)) } }
尝试优化的代码
func Positive[T Positiver](value T, name string, errs *[]error) { switch val := any(value).(type) { case decimal.Decimal: if val.IsNegative() || val.IsZero() { err := fmt.Errorf(`%s %w, but it's not`, name, failures.ShouldBePositive) *errs = append(*errs, err) } return case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: if val.(int64) <= 0 { err := fmt.Errorf(`%s %w, but it's not`, name, failures.ShouldBePositive) *errs = append(*errs, err) } return case float32, float64: if val.(float64) < 0 { err := fmt.Errorf(`%s %w, but it's not`, name, failures.ShouldBePositive) *errs = append(*errs, err) } return default: panic(fmt.Sprintf(`%T is not supported type`, val)) } }
报错信息
测试时触发panic:interface conversion: interface {} is int, not int64(中文翻译:接口类型转换失败:interface{} 实际类型是 int,无法转换为 int64)
优化方案
方案1:分支内细分类型断言(无反射,性能优先)
问题核心是多类型分支中直接强制转换为int64不成立,因为val实际类型可能是int、uint8等基础类型,需在分支内再做一次细分断言:
func Positive[T Positiver](value T, name string, errs *[]error) { addError := func(val any) { *errs = append(*errs, fmt.Errorf(`%s %w, but it's %v`, name, failures.ShouldBePositive, val)) } const precision = 8 switch val := any(value).(type) { case decimal.Decimal: if val.IsNegative() || val.IsZero() { *errs = append(*errs, fmt.Errorf(`%s %w, but it's %s`, name, failures.ShouldBePositive, val.StringFixedBank(precision))) } return case int, int8, int16, int32, int64: var num int64 switch v := val.(type) { case int: num = int64(v) case int8: num = int64(v) case int16: num = int64(v) case int32: num = int64(v) case int64: num = v } if num <= 0 { addError(val) } return case uint, uint8, uint16, uint32, uint64: var num uint64 switch v := val.(type) { case uint: num = uint64(v) case uint8: num = uint64(v) case uint16: num = uint64(v) case uint32: num = uint64(v) case uint64: num = v } if num == 0 { addError(val) } return case float32, float64: var num float64 switch v := val.(type) { case float32: num = float64(v) case float64: num = v } if num <= 0 { addError(val) } return default: panic(fmt.Sprintf(`%T is not supported type`, val)) } }
方案2:利用reflect包统一判断(代码简洁优先)
如果不在意反射的微小性能损耗,可通过reflect.Value直接获取数值并比较:
import "reflect" func Positive[T Positiver](value T, name string, errs *[]error) { addError := func(val any) { *errs = append(*errs, fmt.Errorf(`%s %w, but it's %v`, name, failures.ShouldBePositive, val)) } const precision = 8 val := any(value) switch v := val.(type) { case decimal.Decimal: if v.IsNegative() || v.IsZero() { *errs = append(*errs, fmt.Errorf(`%s %w, but it's %s`, name, failures.ShouldBePositive, v.StringFixedBank(precision))) } return default: rv := reflect.ValueOf(val) var isNonPositive bool switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: isNonPositive = rv.Int() <= 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: isNonPositive = rv.Uint() == 0 case reflect.Float32, reflect.Float64: isNonPositive = rv.Float() <= 0 default: panic(fmt.Sprintf(`%T is not supported type`, val)) } if isNonPositive { addError(val) } } }
方案3:扩展泛型约束添加方法(Go风格最优)
为Positiver约束添加IsPositive()方法,让所有类型实现该方法后,函数内直接调用即可:
type Positiver interface { decimal.Decimal | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64 IsPositive() bool } // 为各基础类型实现IsPositive方法 func (i int) IsPositive() bool { return i > 0 } func (i int8) IsPositive() bool { return i > 0 } func (i int16) IsPositive() bool { return i > 0 } func (i int32) IsPositive() bool { return i > 0 } func (i int64) IsPositive() bool { return i > 0 } func (u uint) IsPositive() bool { return u > 0 } func (u uint8) IsPositive() bool { return u > 0 } func (u uint16) IsPositive() bool { return u > 0 } func (u uint32) IsPositive() bool { return u > 0 } func (u uint64) IsPositive() bool { return u > 0 } func (f float32) IsPositive() bool { return f > 0 } func (f float64) IsPositive() bool { return f > 0 } // 为decimal.Decimal实现IsPositive方法 func (d decimal.Decimal) IsPositive() bool { return !d.IsNegative() && !d.IsZero() } // 简化后的Positive函数 func Positive[T Positiver](value T, name string, errs *[]error) { if !value.IsPositive() { var valStr string if d, ok := any(value).(decimal.Decimal); ok { const precision = 8 valStr = d.StringFixedBank(precision) } else { valStr = fmt.Sprintf("%v", value) } *errs = append(*errs, fmt.Errorf(`%s %w, but it's %s`, name, failures.ShouldBePositive, valStr)) } }
内容的提问来源于stack exchange,提问作者greenif
相关产品推荐
相关产品推荐

