解读Go中的constraints包完整案例

作者:

文章目录
  • constraints 包提供了一系列预定义的约束(constraints),这些约束实际上是接口类型,用于指定泛型类型参数必须满足的条件。
  • 约束 描述 示例类型 Signed 所有有符号整数 int, int8, int64 Unsigned 所有无符号整数 uint, uintptr Integer 所有整数类型 int, uint8 Float 所有浮点数 float32, float64 Complex 所有复数 complex64, complex128 Ordered 可排序类型(支持 < >) int, string, float64 Comparable 可比较类型(支持 == !=)(注:Go 内置了 comparable)
  • package main import ( “fmt” “golang.org/x/exp/constraints” ) // 泛型函数:求最小值(要求类型可排序) func Min[T constraints.Ordered](a, b T) T { if a < b { return a } return b } // 泛型函数:数字绝对值(要求为有符号整数或浮点数) func Abs[T constraints.Signed | constraints.Float](x T) T { if x < 0 { return -x } return x } func main() { fmt.Println(Min(3, 5)) // 3 fmt.Println(Min(“a”, “b”)) // “a” fmt.Println(Abs(-4.5)) // 4.5 }
  • 1.constraints 包目前仍在实验阶段(在 `golang.org/x/exp` 下),未来可能会调整 2.~ 符号表示包含底层类型的类型,例如: ​​​​​ type MyInt int // ~int 包括 int 和所有以 int 为底层类型的类型(如 MyInt) 3. Go 1.18 内置了两个特殊约束: any – 等同于 interface{},任何类型 comparable – 所有可比较的类型 4.实际开发中,如果 constraints 包中的约束不满足需求,可以自定义约束: type Stringish interface { string | fmt.Stringer }
  • Go 的泛型设计强调类型安全,约束机制可以: 明确泛型函数/类型可接受的具体类型 在泛型函数体内明确知道类型参数支持哪些操作 提供更好的编译时类型检查 生成更高效的机器代码 constraints 包提供了一组经过精心设计的常用约束,避免了开发者重复定义这些基本约束。
  • 目录
    • 基本概念
    • 主要约束类型
      • 1. 基本约束
      • 2. 常用组合约束
    • 使用示例
      • 1. 使用 Ordered 约束
      • 2. 自定义约束组合
      • 3.代替方案(无需 constraints包)
    • 表格总结
      •  完整案例示例
        • 注意事项
          • 为什么需要 constraints
            • 与直接定义类型的区别是什么
              • 1. 代码复用性
              • 2. 类型安全
              • 案例对比

            constraints 是 Go 1.18 引入泛型时提供的一个标准包(位于 golang.org/x/exp/constraints),它定义了一组常用的类型约束接口,用于泛型编程中对类型参数进行限制。

            constraints 包提供了一系列预定义的约束(constraints),这些约束实际上是接口类型,用于指定泛型类型参数必须满足的条件。

            • Signed – 所有有符号整数类型
            • int, int8, int16, int32, int64
            // 反转整数符号(正负互换)
            func InvertSign[T constraints.Signed](n T) T {
                return -n
            }
            func main() {
                fmt.Println(InvertSign(-5))  // 输出: 5
                fmt.Println(InvertSign(10))  // 输出: -10
            }
            • Unsigned – 所有无符号整数类型
            • uint, uint8, uint16, uint32, uint64, uintptr
            // 检查是否偶数
            func IsEven[T constraints.Unsigned](n T) bool {
                return n%2 == 0
            }
            func main() {
                fmt.Println(IsEven(uint(4)))  // 输出: true
                fmt.Println(IsEven(uint(7)))  // 输出: false
            }
            • Integer – 所有整数类型(Signed + Unsigned)
            • int, uint8, int64
            // 计算整数平方
            func Square[T constraints.Integer](n T) T {
                return n * n
            }
            func main() {
                fmt.Println(Square(5))      // 输出: 25 (int)
                fmt.Println(Square(uint8(3))) // 输出: 9 (uint8)
            }
            • Float – 所有浮点数类型
            • float32, float64
            // 浮点数四舍五入到整数
            func Round[T constraints.Float](f T) int {
                return int(math.Round(float64(f)))
            }
            func main() {
                fmt.Println(Round(3.14))  // 输出: 3
                fmt.Println(Round(2.78))  // 输出: 3
            }
            • Complex – 所有复数类型
            • complex64, complex128
            // 计算复数模长(|a + bi| = √(a² + b²))
            func Magnitude[T constraints.Complex](c T) float64 {
                r := real(c)
                i := imag(c)
                return math.Sqrt(r*r + i*i)
            }
            func main() {
                c := complex(3.0, 4.0) // 3+4i
                fmt.Println(Magnitude(c)) // 输出: 5 (直角三角形的斜边)
            }

            • Ordered – 所有可比较大小(支持 <<=>>=)的类型
            • int, string, float64
            // 返回两值中的较大值
            func Max[T constraints.Ordered](a, b T) T {
                if a > b {
                    return a
                }
                return b
            }
            func main() {
                fmt.Println(Max(10, 20))          // 输出: 20
                fmt.Println(Max("apple", "banana")) // 输出: "banana"(按字典序)
            }
            • comparable – 内置约束,所有可比较相等性(支持 == 和 !=)的类型
            // 检查值是否在切片中存在
            func Contains[T comparable](slice []T, target T) bool {
                for _, v := range slice {
                    if v == target {  // 依赖 == 操作符
                        return true
                    }
                }
                return false
            }
            func main() {
                names := []string{"Alice", "Bob", "Charlie"}
                fmt.Println(Contains(names, "Bob"))    // 输出: true
                fmt.Println(Contains(names, "David"))   // 输出: false
            }

            import "golang.org/x/exp/constraints"
            func Max[T constraints.Ordered](a, b T) T {
                if a > b {
                    return a
                }
                return b
            }
            // 可以用于整数、浮点数、字符串等
            fmt.Println(Max(1, 2))           // 2
            fmt.Println(Max(3.14, 2.71))    // 3.14
            fmt.Println(Max("apple", "banana")) // "banana"

            type Number interface {
                constraints.Integer | constraints.Float
            }
            func Sum[T Number](a, b T) T {
                return a + b
            }
            fmt.Println(Sum(1, 2))       // 3
            fmt.Println(Sum(1.5, 2.5))   // 4.0
            // fmt.Println(Sum("a", "b")) // 编译错误:string 不满足 Number 约束

            如果不想依赖实验包,可直接内联约束

            // 等效于 constraints.Ordered
            type Ordered interface {
            	~int | ~int8 | ~int16 | ... // 手动列出所有支持的类型
            }
            // 等效于 constraints.Signed
            type Signed interface {
            	~int | ~int8 | ~int16 | ~int32 | ~int64
            }

            约束 描述 示例类型
            Signed 所有有符号整数 intint8int64
            Unsigned 所有无符号整数 uintuintptr
            Integer 所有整数类型 intuint8
            Float 所有浮点数 float32float64
            Complex 所有复数 complex64complex128
            Ordered 可排序类型(支持 < > intstringfloat64
            Comparable 可比较类型(支持 == !=)(注:Go 内置了 comparable

            package main
            import (
            	"fmt"
            	"golang.org/x/exp/constraints"
            )
            // 泛型函数:求最小值(要求类型可排序)
            func Min[T constraints.Ordered](a, b T) T {
            	if a < b {
            		return a
            	}
            	return b
            }
            // 泛型函数:数字绝对值(要求为有符号整数或浮点数)
            func Abs[T constraints.Signed | constraints.Float](x T) T {
            	if x < 0 {
            		return -x
            	}
            	return x
            }
            func main() {
            	fmt.Println(Min(3, 5))      // 3
            	fmt.Println(Min("a", "b"))  // "a"
            	fmt.Println(Abs(-4.5))      // 4.5
            }

            1.constraints 包目前仍在实验阶段(在 `golang.org/x/exp` 下),未来可能会调整

            2.~ 符号表示包含底层类型的类型,例如: ​​​​​

              type MyInt int
               // ~int 包括 int 和所有以 int 为底层类型的类型(如 MyInt)

            3. Go 1.18 内置了两个特殊约束:

            • any – 等同于 interface{},任何类型
            • comparable – 所有可比较的类型

            4.实际开发中,如果 constraints 包中的约束不满足需求,可以自定义约束:

               type Stringish interface {
                   string | fmt.Stringer
               }

            Go 的泛型设计强调类型安全,约束机制可以:

            1. 明确泛型函数/类型可接受的具体类型
            2. 在泛型函数体内明确知道类型参数支持哪些操作
            3. 提供更好的编译时类型检查
            4. 生成更高效的机器代码

            constraints 包提供了一组经过精心设计的常用约束,避免了开发者重复定义这些基本约束。

            • 直接定义类型 针对每种类型重复实现逻辑: ​​​
            func AddInt(a, b int) int { return a + b }
              func AddFloat(a, b float64) float64 { return a + b }

            问题:相同逻辑需为不同类型编写多次,冗余且难维护。

            • 泛型 用类型参数 T 编写通用代码:
              func Add[T int | float64](a, b T) T { return a + b }

            优势:一份代码支持多种类型,减少重复。

            • 直接定义类型 类型固定,安全但缺乏灵活性: ​​​​​
             AddInt(1, 2) // 正确
              AddInt(1.5, 2) // 编译错误
            • 泛型 编译时类型检查确保安全: ​​​​​​
             Add(1, 2)        // T=int
              Add(1.5, 2.0)    // T=float64
              Add("a", "b")    // 编译错误(类型不满足约束)

            优势:在复用代码的同时保持类型安全。

            直接定义类型(冗余)

            type IntStack struct { data []int }
            func (s *IntStack) Push(v int) { s.data = append(s.data, v) }
            type StringStack struct { data []string }
            func (s *StringStack) Push(v string) { ... } // 重复实现

            泛型(复用)

            type Stack[T any] struct { data []T }
            func (s *Stack[T]) Push(v T) { s.data = append(s.data, v) }
            // 使用
            var s1 Stack[int]     // 存储 int
            var s2 Stack[string]  // 存储 string

            到此这篇关于解读 Go 中的 constraints包的文章就介绍到这了,更多相关go constraints包内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

            您可能感兴趣的文章:

            • 使用grpc实现golang后端和python服务间通信
            • golang实现webgis后端开发的步骤详解

            站内搜索