You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优雅比较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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 11:37:04