解讀 Go 中的 constraints包完整案例
constraints 是 Go 1.18 引入泛型時(shí)提供的一個(gè)標(biāo)準(zhǔn)包(位于 golang.org/x/exp/constraints),它定義了一組常用的類型約束接口,用于泛型編程中對(duì)類型參數(shù)進(jìn)行限制。
基本概念
constraints 包提供了一系列預(yù)定義的約束(constraints),這些約束實(shí)際上是接口類型,用于指定泛型類型參數(shù)必須滿足的條件。
主要約束類型
1. 基本約束
Signed- 所有有符號(hào)整數(shù)類型int,int8,int16,int32,int64
// 反轉(zhuǎn)整數(shù)符號(hào)(正負(fù)互換)
func InvertSign[T constraints.Signed](n T) T {
return -n
}
func main() {
fmt.Println(InvertSign(-5)) // 輸出: 5
fmt.Println(InvertSign(10)) // 輸出: -10
}Unsigned- 所有無符號(hào)整數(shù)類型uint,uint8,uint16,uint32,uint64,uintptr
// 檢查是否偶數(shù)
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- 所有整數(shù)類型(Signed + Unsigned)int,uint8,int64
// 計(jì)算整數(shù)平方
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- 所有浮點(diǎn)數(shù)類型float32,float64
// 浮點(diǎn)數(shù)四舍五入到整數(shù)
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- 所有復(fù)數(shù)類型complex64,complex128
// 計(jì)算復(fù)數(shù)模長(|a + bi| = √(a2 + b2))
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 (直角三角形的斜邊)
}2. 常用組合約束
Ordered- 所有可比較大?。ㄖС?nbsp;<,<=,>,>=)的類型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- 內(nèi)置約束,所有可比較相等性(支持==和!=)的類型
// 檢查值是否在切片中存在
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
}使用示例
1. 使用 Ordered 約束
import "golang.org/x/exp/constraints"
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
// 可以用于整數(shù)、浮點(diǎn)數(shù)、字符串等
fmt.Println(Max(1, 2)) // 2
fmt.Println(Max(3.14, 2.71)) // 3.14
fmt.Println(Max("apple", "banana")) // "banana"2. 自定義約束組合
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")) // 編譯錯(cuò)誤:string 不滿足 Number 約束3.代替方案(無需 constraints包)
如果不想依賴實(shí)驗(yàn)包,可直接內(nèi)聯(lián)約束:
// 等效于 constraints.Ordered
type Ordered interface {
~int | ~int8 | ~int16 | ... // 手動(dòng)列出所有支持的類型
}
// 等效于 constraints.Signed
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}表格總結(jié)
| 約束 | 描述 | 示例類型 |
|---|---|---|
Signed | 所有有符號(hào)整數(shù) | int, int8, int64 |
Unsigned | 所有無符號(hào)整數(shù) | uint, uintptr |
Integer | 所有整數(shù)類型 | int, uint8 |
Float | 所有浮點(diǎn)數(shù) | float32, float64 |
Complex | 所有復(fù)數(shù) | complex64, complex128 |
Ordered | 可排序類型(支持 < >) | int, string, float64 |
Comparable | 可比較類型(支持 == !=)(注:Go 內(nèi)置了 comparable) |
完整案例示例
package main
import (
"fmt"
"golang.org/x/exp/constraints"
)
// 泛型函數(shù):求最小值(要求類型可排序)
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
// 泛型函數(shù):數(shù)字絕對(duì)值(要求為有符號(hào)整數(shù)或浮點(diǎn)數(shù))
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
}注意事項(xiàng)
1.constraints 包目前仍在實(shí)驗(yàn)階段(在 `golang.org/x/exp` 下),未來可能會(huì)調(diào)整
2.~ 符號(hào)表示包含底層類型的類型,例如: ?????
type MyInt int // ~int 包括 int 和所有以 int 為底層類型的類型(如 MyInt)
3. Go 1.18 內(nèi)置了兩個(gè)特殊約束:
any- 等同于interface{},任何類型comparable- 所有可比較的類型
4.實(shí)際開發(fā)中,如果 constraints 包中的約束不滿足需求,可以自定義約束:
type Stringish interface {
string | fmt.Stringer
}為什么需要 constraints
Go 的泛型設(shè)計(jì)強(qiáng)調(diào)類型安全,約束機(jī)制可以:
- 明確泛型函數(shù)/類型可接受的具體類型
- 在泛型函數(shù)體內(nèi)明確知道類型參數(shù)支持哪些操作
- 提供更好的編譯時(shí)類型檢查
- 生成更高效的機(jī)器代碼
constraints 包提供了一組經(jīng)過精心設(shè)計(jì)的常用約束,避免了開發(fā)者重復(fù)定義這些基本約束。
與直接定義類型的區(qū)別是什么
1. 代碼復(fù)用性
- 直接定義類型 針對(duì)每種類型重復(fù)實(shí)現(xiàn)邏輯: ???
func AddInt(a, b int) int { return a + b }
func AddFloat(a, b float64) float64 { return a + b }問題:相同邏輯需為不同類型編寫多次,冗余且難維護(hù)。
- 泛型 用類型參數(shù)
T編寫通用代碼:
func Add[T int | float64](a, b T) T { return a + b }優(yōu)勢:一份代碼支持多種類型,減少重復(fù)。
2. 類型安全
- 直接定義類型 類型固定,安全但缺乏靈活性: ?????
AddInt(1, 2) // 正確 AddInt(1.5, 2) // 編譯錯(cuò)誤
- 泛型 編譯時(shí)類型檢查確保安全: ??????
Add(1, 2) // T=int
Add(1.5, 2.0) // T=float64
Add("a", "b") // 編譯錯(cuò)誤(類型不滿足約束)優(yōu)勢:在復(fù)用代碼的同時(shí)保持類型安全。
案例對(duì)比
直接定義類型(冗余)
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) { ... } // 重復(fù)實(shí)現(xiàn)泛型(復(fù)用)
type Stack[T any] struct { data []T }
func (s *Stack[T]) Push(v T) { s.data = append(s.data, v) }
// 使用
var s1 Stack[int] // 存儲(chǔ) int
var s2 Stack[string] // 存儲(chǔ) string到此這篇關(guān)于解讀 Go 中的 constraints包的文章就介紹到這了,更多相關(guān)go constraints包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文詳細(xì)談?wù)凣oLang的panic和error
說是初識(shí),并不是說第一次使用error和panic包,而是第一次特地去了解golang中的這兩個(gè)機(jī)制,下面這篇文章主要給大家介紹了關(guān)于如何通過一文詳細(xì)談?wù)凣oLang中panic和error的相關(guān)資料,需要的朋友可以參考下2022-12-12
golang基礎(chǔ)之waitgroup用法以及使用要點(diǎn)
WaitGroup是Golang并發(fā)的兩種方式之一,一個(gè)是Channel,另一個(gè)是WaitGroup,下面這篇文章主要給大家介紹了關(guān)于golang基礎(chǔ)之waitgroup用法以及使用要點(diǎn)的相關(guān)資料,需要的朋友可以參考下2023-01-01
深入了解Golang網(wǎng)絡(luò)編程N(yùn)et包的使用
net包主要是增加?context?控制,封裝了一些不同的連接類型以及DNS?查找等等,同時(shí)在有需要的地方引入?goroutine?提高處理效率。本文主要和大家分享下在Go中網(wǎng)絡(luò)編程的實(shí)現(xiàn),需要的可以參考一下2022-07-07

