Golang中generic泛型的实战指南

 更新时间:2026年06月29日 09:41:48   作者:唐青枫  
本文详细介绍了Go语言中的泛型功能,包括泛型函数、类型约束、自定义约束及常见应用场景,如和泛型相比,接口更适合描述行为,泛型则更适合复用类型安全的算法和数据结构

简介

generic 通常翻译成“泛型”。

Go 从 1.18 开始支持泛型。

泛型解决的核心问题很直接:同一套逻辑,适配多种类型,同时保留编译期类型检查。

比如求和。

没有泛型时,int 要写一份:

func SumInts(nums []int) int {
	var total int

	for _, n := range nums {
		total += n
	}

	return total
}

float64 又要写一份:

func SumFloat64s(nums []float64) float64 {
	var total float64

	for _, n := range nums {
		total += n
	}

	return total
}

逻辑完全一样,只是类型不同。

使用泛型后,可以合并成一个函数:

func Sum[T int | float64](nums []T) T {
	var total T

	for _, n := range nums {
		total += n
	}

	return total
}

一句话概括:

泛型让代码把“类型”也当成参数传进去。

第一个泛型函数

先看一个最简单的泛型函数:

package main

import "fmt"

func Identity[T any](value T) T {
	return value
}

func main() {
	fmt.Println(Identity(100))
	fmt.Println(Identity("hello"))
	fmt.Println(Identity(true))
}

输出:

100
hello
true

这里的核心是:

func Identity[T any](value T) T

拆开看:

部分含义
T类型参数名称
any类型约束,表示任意类型
value T参数类型是 T
返回值 T返回值类型也是 T

Identity(100) 调用时,T 会被推断成 int

Identity("hello") 调用时,T 会被推断成 string

类型参数是什么

普通函数的参数是“值参数”。

func Add(a int, b int) int {
	return a + b
}

ab 是值参数。

泛型函数多了一类参数,叫“类型参数”。

func Add[T int | int64](a T, b T) T {
	return a + b
}

T 是类型参数。

它不是某个具体值,而是某个具体类型的占位符。

调用时:

Add(1, 2)

编译器推断出:

T = int

调用:

Add(int64(1), int64(2))

编译器推断出:

T = int64

any 约束

anyinterface{} 的别名。

type any = interface{}

它表示任意类型。

package main

import "fmt"

func Print[T any](value T) {
	fmt.Printf("type=%T value=%v\n", value, value)
}

func main() {
	Print(100)
	Print("go")
	Print([]int{1, 2, 3})
}

输出:

type=int value=100
type=string value=go
type=[]int value=[1 2 3]

any 也有限制。

它只表示“任意类型”,不代表这个类型支持所有操作。

下面这种代码不能通过编译:

func Add[T any](a T, b T) T {
	return a + b
}

原因是:

any 不保证 T 支持 + 运算。

如果要使用 +,必须给类型参数加更具体的约束。

类型约束

泛型里的约束用来限制类型参数能是什么类型。

例如:

func Add[T int | int64 | float64](a T, b T) T {
	return a + b
}

这里的:

T int | int64 | float64

表示:

T 只能是 int、int64、float64 这几种类型之一。

因为这些类型都支持 +,所以函数体里可以写:

return a + b

完整示例:

package main

import "fmt"

func Add[T int | int64 | float64](a T, b T) T {
	return a + b
}

func main() {
	fmt.Println(Add(1, 2))
	fmt.Println(Add(int64(10), int64(20)))
	fmt.Println(Add(1.5, 2.5))
}

输出:

3
30
4

自定义约束

如果很多函数都要使用同一组数值类型,可以把约束抽出来。

type Number interface {
	int | int64 | float64
}

然后复用:

func Add[T Number](a T, b T) T {
	return a + b
}

func Sum[T Number](nums []T) T {
	var total T

	for _, n := range nums {
		total += n
	}

	return total
}

完整示例:

package main

import "fmt"

type Number interface {
	int | int64 | float64
}

func Sum[T Number](nums []T) T {
	var total T

	for _, n := range nums {
		total += n
	}

	return total
}

func main() {
	fmt.Println(Sum([]int{1, 2, 3}))
	fmt.Println(Sum([]int64{10, 20, 30}))
	fmt.Println(Sum([]float64{1.1, 2.2, 3.3}))
}

输出:

6
60
6.6

~ 是什么

~ 用来匹配底层类型。

先看一个自定义类型:

type UserID int64

UserID 是一个新类型,它的底层类型是 int64

如果约束写成:

type Integer interface {
	int64
}

那么 UserID 不满足这个约束。

因为 UserID 不是 int64,只是底层类型是 int64

如果希望支持这种自定义类型,要写:

type Integer interface {
	~int64
}

完整示例:

package main

import "fmt"

type UserID int64

type Integer interface {
	~int | ~int64
}

func Add[T Integer](a T, b T) T {
	return a + b
}

func main() {
	var a UserID = 100
	var b UserID = 200

	fmt.Println(Add(a, b))
}

输出:

300

可以这样记:

  • int64 只匹配 int64。
  • ~int64 匹配 int64,以及底层类型是 int64 的自定义类型。

comparable 约束

comparable 是 Go 内置约束。

它表示类型可以使用 ==!= 比较。

常见可比较类型:

  • 数字
  • 字符串
  • 布尔
  • 指针
  • channel
  • 字段都可比较的结构体

slice、map、func 不能比较。

泛型里经常用 comparable 写查找函数:

package main

import "fmt"

func Contains[T comparable](items []T, target T) bool {
	for _, item := range items {
		if item == target {
			return true
		}
	}

	return false
}

func main() {
	fmt.Println(Contains([]int{1, 2, 3}, 2))
	fmt.Println(Contains([]string{"go", "java"}, "php"))
}

输出:

true
false

comparable 也常用于 map key。

func Keys[K comparable, V any](m map[K]V) []K {
	keys := make([]K, 0, len(m))

	for k := range m {
		keys = append(keys, k)
	}

	return keys
}

map 的 key 必须可比较,所以这里的 K 要写成 comparable

类型推断

调用泛型函数时,很多时候不用手动写类型。

fmt.Println(Contains([]int{1, 2, 3}, 2))

编译器可以根据参数推断:

T = int

也可以显式指定:

fmt.Println(Contains[int]([]int{1, 2, 3}, 2))

多数业务代码不需要显式指定。

但如果类型参数只出现在返回值里,编译器通常推断不出来。

func Zero[T any]() T {
	var zero T
	return zero
}

调用时需要写类型:

v := Zero[int]()

泛型切片工具:Map

Map 用来把一个切片转换成另一个切片。

例如把 []int 转成 []string

package main

import "fmt"

func Map[T any, R any](items []T, mapper func(T) R) []R {
	result := make([]R, 0, len(items))

	for _, item := range items {
		result = append(result, mapper(item))
	}

	return result
}

func main() {
	nums := []int{1, 2, 3}

	texts := Map(nums, func(n int) string {
		return fmt.Sprintf("num=%d", n)
	})

	fmt.Println(texts)
}

输出:

[num=1 num=2 num=3]

这个函数里有两个类型参数:

func Map[T any, R any](items []T, mapper func(T) R) []R

含义:

  • T 是输入元素类型
  • R 是输出元素类型

泛型切片工具:Filter

Filter 用来过滤切片。

package main

import "fmt"

func Filter[T any](items []T, predicate func(T) bool) []T {
	result := make([]T, 0, len(items))

	for _, item := range items {
		if predicate(item) {
			result = append(result, item)
		}
	}

	return result
}

func main() {
	nums := []int{1, 2, 3, 4, 5}

	evens := Filter(nums, func(n int) bool {
		return n%2 == 0
	})

	fmt.Println(evens)
}

输出:

[2 4]

同一个 Filter 也能过滤结构体切片:

type User struct {
	ID     int
	Name   string
	Active bool
}

users := []User{
	{ID: 1, Name: "张三", Active: true},
	{ID: 2, Name: "李四", Active: false},
}

activeUsers := Filter(users, func(u User) bool {
	return u.Active
})

泛型切片工具:Find

Find 用来查找第一个满足条件的元素。

找到了返回元素和 true

没找到返回零值和 false

package main

import "fmt"

func Find[T any](items []T, predicate func(T) bool) (T, bool) {
	var zero T

	for _, item := range items {
		if predicate(item) {
			return item, true
		}
	}

	return zero, false
}

type User struct {
	ID   int
	Name string
}

func main() {
	users := []User{
		{ID: 1, Name: "张三"},
		{ID: 2, Name: "李四"},
	}

	user, ok := Find(users, func(u User) bool {
		return u.ID == 2
	})

	fmt.Println(user, ok)
}

输出:

{2 李四} true

泛型切片工具:Reduce

Reduce 用来把一组数据归约成一个结果。

例如求总和、拼接字符串、统计数量。

package main

import "fmt"

func Reduce[T any, R any](items []T, initial R, reducer func(R, T) R) R {
	result := initial

	for _, item := range items {
		result = reducer(result, item)
	}

	return result
}

func main() {
	nums := []int{1, 2, 3, 4}

	total := Reduce(nums, 0, func(sum int, n int) int {
		return sum + n
	})

	fmt.Println(total)
}

输出:

10

泛型结构体

结构体也可以带类型参数。

最简单的是 Box

package main

import "fmt"

type Box[T any] struct {
	Value T
}

func (b Box[T]) Get() T {
	return b.Value
}

func (b *Box[T]) Set(value T) {
	b.Value = value
}

func main() {
	intBox := Box[int]{Value: 100}
	fmt.Println(intBox.Get())

	stringBox := Box[string]{Value: "hello"}
	stringBox.Set("go")
	fmt.Println(stringBox.Get())
}

输出:

100
go

定义泛型类型时:

type Box[T any] struct

给泛型类型定义方法时,接收者也要带上类型参数:

func (b Box[T]) Get() T
func (b *Box[T]) Set(value T)

实战 Demo:泛型 Stack

栈是典型的泛型数据结构。

int 栈、string 栈、User 栈,逻辑都一样。

package main

import "fmt"

type Stack[T any] struct {
	items []T
}

func NewStack[T any]() *Stack[T] {
	return &Stack[T]{
		items: make([]T, 0),
	}
}

func (s *Stack[T]) Push(item T) {
	s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
	if len(s.items) == 0 {
		var zero T
		return zero, false
	}

	lastIndex := len(s.items) - 1
	item := s.items[lastIndex]
	s.items = s.items[:lastIndex]

	return item, true
}

func (s *Stack[T]) Len() int {
	return len(s.items)
}

func main() {
	stack := NewStack[int]()

	stack.Push(10)
	stack.Push(20)

	v, ok := stack.Pop()
	fmt.Println(v, ok)
	fmt.Println(stack.Len())
}

输出:

20 true
1

实战 Demo:泛型 Set

Go 没有内置 Set,可以用 map[T]struct{} 封装。

因为 map key 必须可比较,所以 T 需要 comparable 约束。

package main

import "fmt"

type Set[T comparable] struct {
	data map[T]struct{}
}

func NewSet[T comparable](items ...T) *Set[T] {
	set := &Set[T]{
		data: make(map[T]struct{}, len(items)),
	}

	for _, item := range items {
		set.Add(item)
	}

	return set
}

func (s *Set[T]) Add(item T) {
	s.data[item] = struct{}{}
}

func (s *Set[T]) Remove(item T) {
	delete(s.data, item)
}

func (s *Set[T]) Contains(item T) bool {
	_, ok := s.data[item]
	return ok
}

func (s *Set[T]) Values() []T {
	values := make([]T, 0, len(s.data))

	for item := range s.data {
		values = append(values, item)
	}

	return values
}

func main() {
	ids := NewSet(1, 2, 2, 3)

	fmt.Println(ids.Contains(2))
	fmt.Println(ids.Values())

	names := NewSet("go", "java", "go")
	fmt.Println(names.Contains("php"))
	fmt.Println(names.Values())
}

输出类似:

true
[1 2 3]
false
[go java]

map 遍历顺序不固定,所以 Values() 输出顺序可能变化。

实战 Demo:通用 API 响应

Web 项目里经常有统一响应结构。

没有泛型时,Data 可能写成 any

type ApiResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    any    `json:"data"`
}

这种写法灵活,但丢失了 Data 的具体类型。

使用泛型可以保留类型:

type ApiResponse[T any] struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    T      `json:"data"`
}

完整示例:

package main

import (
	"encoding/json"
	"fmt"
)

type ApiResponse[T any] struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    T      `json:"data"`
}

type User struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func Success[T any](data T) ApiResponse[T] {
	return ApiResponse[T]{
		Code:    200,
		Message: "success",
		Data:    data,
	}
}

func main() {
	resp := Success(User{ID: 1, Name: "张三"})

	data, err := json.Marshal(resp)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(data))
}

输出:

{"code":200,"message":"success","data":{"id":1,"name":"张三"}}

实战 Demo:分页结果

分页结果也很适合泛型。

用户分页、商品分页、订单分页,结构基本一样,只是列表元素类型不同。

package main

import (
	"encoding/json"
	"fmt"
)

type PageResult[T any] struct {
	Page     int   `json:"page"`
	PageSize int   `json:"pageSize"`
	Total    int64 `json:"total"`
	Items    []T   `json:"items"`
}

type Product struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Price int    `json:"price"`
}

func NewPageResult[T any](page int, pageSize int, total int64, items []T) PageResult[T] {
	return PageResult[T]{
		Page:     page,
		PageSize: pageSize,
		Total:    total,
		Items:    items,
	}
}

func main() {
	result := NewPageResult(1, 10, 100, []Product{
		{ID: 1, Name: "键盘", Price: 199},
		{ID: 2, Name: "鼠标", Price: 99},
	})

	data, err := json.Marshal(result)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(data))
}

输出:

{"page":1,"pageSize":10,"total":100,"items":[{"id":1,"name":"键盘","price":199},{"id":2,"name":"鼠标","price":99}]}

实战 Demo:通用内存 Repository

泛型也可以用来封装一些通用仓储逻辑。

先定义一个实体约束:

type Entity interface {
	GetID() int64
}

然后 Repository 只接收实现了 GetID 的类型。

完整示例:

package main

import "fmt"

type Entity interface {
	GetID() int64
}

type Repository[T Entity] struct {
	data map[int64]T
}

func NewRepository[T Entity]() *Repository[T] {
	return &Repository[T]{
		data: make(map[int64]T),
	}
}

func (r *Repository[T]) Save(entity T) {
	r.data[entity.GetID()] = entity
}

func (r *Repository[T]) FindByID(id int64) (T, bool) {
	entity, ok := r.data[id]
	return entity, ok
}

func (r *Repository[T]) List() []T {
	result := make([]T, 0, len(r.data))

	for _, entity := range r.data {
		result = append(result, entity)
	}

	return result
}

type User struct {
	ID   int64
	Name string
}

func (u User) GetID() int64 {
	return u.ID
}

type Product struct {
	ID    int64
	Name  string
	Price int
}

func (p Product) GetID() int64 {
	return p.ID
}

func main() {
	userRepo := NewRepository[User]()
	userRepo.Save(User{ID: 1, Name: "张三"})

	user, ok := userRepo.FindByID(1)
	fmt.Println(user, ok)

	productRepo := NewRepository[Product]()
	productRepo.Save(Product{ID: 100, Name: "键盘", Price: 199})

	product, ok := productRepo.FindByID(100)
	fmt.Println(product, ok)
}

输出:

{1 张三} true
{100 键盘 199} true

这种写法适合演示通用逻辑。

真实业务里的 Repository 往往会有复杂查询、事务、权限、缓存等差异,不一定都适合硬抽成泛型。

泛型接口

接口也可以带类型参数。

例如定义一个通用 Store:

type Store[T any] interface {
	Save(value T) error
	Find() (T, bool)
}

完整示例:

package main

import "fmt"

type Store[T any] interface {
	Save(value T) error
	Find() (T, bool)
}

type MemoryStore[T any] struct {
	value T
	ok    bool
}

func (s *MemoryStore[T]) Save(value T) error {
	s.value = value
	s.ok = true
	return nil
}

func (s *MemoryStore[T]) Find() (T, bool) {
	return s.value, s.ok
}

func main() {
	var store Store[string] = &MemoryStore[string]{}

	_ = store.Save("hello")

	value, ok := store.Find()
	fmt.Println(value, ok)
}

输出:

hello true

泛型方法的注意点

Go 支持给泛型类型定义方法。

type Box[T any] struct {
	Value T
}

func (b Box[T]) Get() T {
	return b.Value
}

但方法不能单独声明一组新的类型参数。

下面这种写法不能编译:

func (b Box[T]) Map[R any](mapper func(T) R) R {
	return mapper(b.Value)
}

这类需求可以改成普通泛型函数:

func MapBox[T any, R any](b Box[T], mapper func(T) R) R {
	return mapper(b.Value)
}

泛型 vs interface

interface 适合表达行为。

type Writer interface {
	Write(p []byte) (int, error)
}

重点是:对象能做什么。

泛型适合保留具体类型并复用算法。

func First[T any](items []T) (T, bool) {
	if len(items) == 0 {
		var zero T
		return zero, false
	}

	return items[0], true
}

重点是:同一套代码处理不同类型,同时返回具体类型。

对比:

func FirstAny(items []any) (any, bool) {
	if len(items) == 0 {
		return nil, false
	}

	return items[0], true
}

FirstAny 返回的是 any,调用方还要类型断言。

泛型版本返回的是准确类型:

value, ok := First([]int{1, 2, 3})

这里的 valueint

标准库里的泛型工具

Go 标准库里已经有一些泛型工具包,例如 slicesmaps

示例:

package main

import (
	"fmt"
	"slices"
)

func main() {
	nums := []int{3, 1, 2}

	slices.Sort(nums)

	fmt.Println(nums)
	fmt.Println(slices.Contains(nums, 2))
}

输出:

[1 2 3]
true

这些工具能直接处理不同元素类型的切片。

日常项目里,能使用标准库工具时,没必要重复封装一套。

什么时候适合用泛型

适合使用泛型的场景:

  • 多种类型共享同一套逻辑
  • 返回值需要保留具体类型
  • 集合工具函数,例如 MapFilterContains
  • 通用数据结构,例如 SetStackQueue
  • 通用返回结构,例如 ApiResponse[T]PageResult[T]
  • 类型安全比 any + 类型断言更重要

示例:

func Contains[T comparable](items []T, target T) bool
type Set[T comparable] struct
type PageResult[T any] struct

什么时候不适合用泛型

不适合泛型化的场景:

  • 只有一个类型会使用
  • 各类型之间业务差异很大
  • 类型约束写得特别复杂
  • 泛型让调用方更难读懂
  • 为了少写几行代码而牺牲清晰度

比如两个业务流程只是表面类似,但校验、查询、事务、日志完全不同,强行抽成泛型会让代码变绕。

这种情况下,普通函数或接口可能更合适。

常见问题

any 和泛型是一回事吗

不是。

any 是一个约束,也可以作为普通接口类型使用。

泛型是“带类型参数的代码”。

例如:

func Print[T any](value T)

这里使用了泛型,any 只是 T 的约束。

而下面这个函数不是泛型:

func Print(value any)

它只是接收一个空接口值。

comparable 可以表示所有能排序的类型吗

不能。

comparable 只表示可以用 ==!= 比较。

它不代表可以用 <> 排序。

例如布尔值可以比较相等,但不能排序。

如果需要 <>,需要使用自定义约束:

type Ordered interface {
	~int | ~int64 | ~float64 | ~string
}

泛型类型可以自动推断类型参数吗

泛型函数通常可以通过参数推断。

Contains([]int{1, 2, 3}, 2)

泛型类型初始化时通常需要写出类型参数:

stack := Stack[int]{}

如果通过构造函数,函数可以帮助推断一部分类型。

set := NewSet(1, 2, 3)

这里 NewSet 是泛型函数,编译器可以根据传入参数推断 T

泛型能完全替代 interface 吗

不能。

泛型和接口解决的问题不同。

接口更适合描述行为和解耦实现。

泛型更适合复用类型安全的算法和数据结构。

例如 io.Reader 这种“能读取”的行为抽象,用接口更自然。

例如 Set[T] 这种“相同逻辑适配不同元素类型”的容器,用泛型更自然。

总结

generic 的核心可以压缩成几句话:

  • 泛型让类型也能成为参数。
  • 类型参数写在 [] 里。
  • 约束限制类型参数能做什么。
  • any 表示任意类型。
  • comparable 表示支持 == 和 !=。
  • ~T 表示包含底层类型是 T 的自定义类型。
  • 泛型适合通用算法、通用容器、通用响应结构。

日常选择可以按下面这张表判断:

场景常见写法
任意类型参数[T any]
可比较类型[T comparable]
数值计算自定义 Number 约束
支持自定义底层类型~int、~string
多类型联合`int
通用切片转换Map[T, R]
通用切片过滤Filter[T]
通用集合Set[T comparable]
通用响应ApiResponse[T]
通用分页PageResult[T]
行为抽象interface 更合适

泛型不是越多越好。它最适合消除真正重复的类型逻辑,并且让返回值保持具体类型。代码只有一个类型会用、业务规则差异很大、约束写得很绕时,直接写普通代码通常更清楚。

以上就是Golang中generic泛型的实战指南的详细内容,更多关于Golang generic泛型的资料请关注脚本之家其它相关文章!

相关文章

  • golang bad file descriptor问题的解决方法

    golang bad file descriptor问题的解决方法

    这篇文章主要给大家介绍了golang bad file descriptor问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-02-02
  • VSCode1.4 搭建Golang的开发调试环境(遇到很多问题)

    VSCode1.4 搭建Golang的开发调试环境(遇到很多问题)

    这篇文章主要介绍了VSCode1.4 搭建Golang的开发调试环境(遇到很多问题),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • Go 在 MongoDB 中常用查询与修改的操作

    Go 在 MongoDB 中常用查询与修改的操作

    这篇文章主要介绍了Go 在 MongoDB 中常用查询与修改的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • Go Java 算法之字符串解码示例详解

    Go Java 算法之字符串解码示例详解

    这篇文章主要为大家介绍了Go Java 算法之字符串解码示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • golang关闭chan通道的方法示例

    golang关闭chan通道的方法示例

    在go语言中,通道(channel)是一个非常重要的概念,通道提供了一种在不同 goroutine 之间安全地传递数据的方式,在本文中,我们将讨论如何关闭通道以及在关闭通道时需要考虑的事项,需要的朋友可以参考下
    2024-02-02
  • 详解golang中 work与 module 的区别与联系

    详解golang中 work与 module 的区别与联系

    Go 模块通常由一个项目或库组成,并包含一组随后一起发布的 Go 包,Go 模块通过允许用户将项目代码放在他们选择的目录中并为每个模块指定依赖项的版本,解决了原始系统的许多问题,本文将给大家介绍一下golang中 work与 module 的区别与联系,需要的朋友可以参考下
    2023-09-09
  • 深入解析golang bufio

    深入解析golang bufio

    这篇文章主要介绍了golang bufio解析,golang的bufio库使用缓存来一次性进行大块数据的读写,以此降低IO系统调用,提升性能,需要的朋友可以参考下
    2022-04-04
  • 浅谈Golang 切片(slice)扩容机制的原理

    浅谈Golang 切片(slice)扩容机制的原理

    我们知道 Golang 切片在容量不足的情况下会进行扩容,扩容的原理是怎样的呢,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • 详解golang的切片扩容机制

    详解golang的切片扩容机制

    golang的切片扩容机制是golang面试者绕不开的一扇大门,无论在面试提问,或者面试情景上都绕不开它,今天就说说我理解下的切片扩容机制,感兴趣的小伙伴跟着小编一起来看看吧
    2023-07-07
  • golang定时器Timer的用法和实现原理解析

    golang定时器Timer的用法和实现原理解析

    这篇文章主要介绍了golang定时器Ticker,本文主要来看一下Timer的用法和实现原理,需要的朋友可以参考以下内容
    2023-04-04

最新评论