幸福转角 發表於 2025-12-5 09:07:34

Go泛型中的~struct{}的具体使用

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一、前置知识:Go泛型的核心痛点与解决方案</a></li><li><a href="#_label1">二、~符号:引入版本与核心目的</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">2.1 ~符号的引入版本</a></li><li><a href="#_lab2_1_1">2.2 ~符号的核心目的:实现&ldquo;近似类型匹配&rdquo;</a></li></ul><li><a href="#_label2">三、struct{}:&ldquo;零内存&rdquo;的空结构体特性</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_2">3.1 struct{}的内存特性验证</a></li><li><a href="#_lab2_2_3">3.2 struct{}的典型应用场景</a></li></ul><li><a href="#_label3">四、~struct{}:约束含义与实践示例</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_4">4.1 示例1:精确约束struct{} vs 近似约束~struct{}</a></li><li><a href="#_lab2_3_5">4.2 示例2:~struct{}在泛型集合中的应用</a></li><li><a href="#_lab2_3_6">4.3 示例3:~struct{}在通道信号处理中的应用</a></li></ul><li><a href="#_label4">五、知识扩展:~符号的更多泛型约束用法</a></li><ul class="second_class_ul"><li><a href="#_lab2_4_7">5.1 ~与基本类型结合</a></li><li><a href="#_lab2_4_8">5.2 ~与接口结合</a></li><li><a href="#_lab2_4_9">5.3 ~与联合约束结合</a></li></ul><li><a href="#_label5">六、实践场景: Gin 路由自动注入</a></li><ul class="second_class_ul"></ul><li><a href="#_label6">七、总结</a></li><ul class="second_class_ul"></ul></ul></div><p>自<strong>Go 1.18</strong>版本正式引入泛型以来,Go语言的类型系统得到了极大丰富,开发者终于可以摆脱&ldquo;重复代码&rdquo;的困扰,用更抽象、更通用的方式编写代码。在Go泛型的类型约束体系中,<code>~</code>符号是一个极具代表性的特殊符号,而<code>struct{}</code>作为Go语言中&ldquo;不占内存&rdquo;的空结构体,两者结合而成的<code>~struct{}</code>约束,在特定场景下有着独特的应用价值。本文将从基础概念出发,逐步深入研究~struct{},明确~符号的引入版本与核心目的,结合示例代码详解其用法,并扩展相关泛型知识,帮助读者彻底掌握这一技术点。</p>
<p class="maodian"><a name="_label0"></a></p><h2>一、前置知识:Go泛型的核心痛点与解决方案</h2>
<p>在Go 1.18之前,Go语言并不支持泛型,这导致开发者在处理&ldquo;相同逻辑但不同类型&rdquo;的场景时,只能通过两种方式解决:一是使用<code>interface{}</code>(空接口)搭配类型断言,这种方式会丢失编译时类型检查,增加运行时错误风险;二是针对不同类型重复编写相似代码,导致代码冗余、维护成本高。</p>
<p>例如,要实现一个&ldquo;获取切片中第一个元素&rdquo;的功能,针对<code>[]int</code>、<code>[]string</code>、<code>[]float64</code>三种类型,需要编写三个几乎完全相同的函数:</p>
<div class="jb51code"><pre class="brush:go;">// 获取[]int切片的第一个元素
func FirstInt(s []int) (int, error) {
    if len(s) == 0 {
      return 0, errors.New("slice is empty")
    }
    return s, nil
}

// 获取[]string切片的第一个元素
func FirstString(s []string) (string, error) {
    if len(s) == 0 {
      return "", errors.New("slice is empty")
    }
    return s, nil
}

// 获取[]float64切片的第一个元素
func FirstFloat64(s []float64) (float64, error) {
    if len(s) == 0 {
      return 0, errors.New("slice is empty")
    }
    return s, nil
}
</pre></div>
<p>这种方式的弊端显而易见。泛型的引入,正是为了解决这一问题&mdash;&mdash;通过定义&ldquo;类型参数&rdquo;,让函数或结构体能够适配多种类型,同时保留编译时类型检查。而<code>~</code>符号,就是Go泛型类型约束体系中,为解决&ldquo;类型匹配灵活性&rdquo;问题而设计的核心语法。</p>
<p class="maodian"><a name="_label1"></a></p><h2>二、~符号:引入版本与核心目的</h2>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>2.1 ~符号的引入版本</h3>
<p><strong><code>~</code></strong> 符号是随着Go 1.18版本(于2022年3月发布)正式引入的,与泛型特性同步推出。Go 1.18是Go语言发展史上的一个重要里程碑,除了泛型,还包含了模块工作区、模糊测试等关键特性,而~符号作为泛型类型约束的&ldquo;近似匹配&rdquo;运算符,是泛型功能得以灵活使用的重要基础。</p>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>2.2 ~符号的核心目的:实现&ldquo;近似类型匹配&rdquo;</h3>
<p>在Go泛型中,类型约束的核心作用是&ldquo;限制类型参数的取值范围&rdquo;。没有~符号时,类型约束采用的是 <strong><code>&ldquo;精确匹配&rdquo;</code></strong> 规则&mdash;&mdash;即类型参数必须与约束中的类型完全一致(或实现了约束中的接口)。<br />而~符号的核心目的,是 将&ldquo;精确匹配&rdquo;升级为&ldquo;近似匹配&rdquo;,允许类型参数是&ldquo;约束类型的底层类型相同的衍生类型&rdquo;。</p>
<p>首先需要明确Go中的&ldquo;底层类型&rdquo;概念:</p>
<ul><li>基本类型(如int、string、bool)的底层类型就是其自身;</li><li>通过<code>type 新类型 底层类型</code>定义的衍生类型,其底层类型为定义时指定的类型。</li></ul>
<p>例如:</p>
<div class="jb51code"><pre class="brush:go;">// MyInt的底层类型是int
type MyInt int

// UserName的底层类型是string
type UserName string

// EmptyStruct的底层类型是struct{}
type EmptyStruct struct{}
</pre></div>
<p>在没有~符号的约束中,若约束为<code>int</code>,则类型参数只能是<code>int</code>,不能是<code>MyInt</code>(尽管两者底层类型相同);若约束为<code>~int</code>,则类型参数可以是<code>int</code>,也可以是所有底层类型为<code>int</code>的衍生类型(如<code>MyInt</code>)。</p>
<p>简单来说,~符号的作用是:<strong>约束类型参数的&ldquo;底层类型&rdquo;必须是指定类型,而不要求类型参数与指定类型完全一致</strong>。这一特性极大地提升了泛型的灵活性,让开发者可以基于底层类型设计通用逻辑,适配更多衍生类型场景。</p>
<p class="maodian"><a name="_label2"></a></p><h2>三、struct{}:&ldquo;零内存&rdquo;的空结构体特性</h2>
<p>在研究~struct{}之前,我们需要先掌握<code>struct{}</code>的核心特性&mdash;&mdash;它是Go语言中一种特殊的结构体类型,被称为&ldquo;空结构体&rdquo;,其最显著的特点是:<strong>不占用任何内存空间</strong>。</p>
<p class="maodian"><a name="_lab2_2_2"></a></p><h3>3.1 struct{}的内存特性验证</h3>
<p>我们可以通过<code>unsafe.Sizeof()</code>函数(用于获取变量的内存占用大小)验证struct{}的内存特性:</p>
<div class="jb51code"><pre class="brush:go;">package main

import (
    "fmt"
    "unsafe"
)

func main() {
    // 空结构体变量
    var s struct{}
    // 空结构体指针
    var p *struct{}

    fmt.Printf("struct{} 大小:%d 字节\n", unsafe.Sizeof(s))
    fmt.Printf("*struct{} 大小:%d 字节\n", unsafe.Sizeof(p))
}
</pre></div>
<p>运行结果(不同架构下指针大小可能不同,此处以64位架构为例):</p>
<div class="jb51code"><pre class="brush:go;">struct{} 大小:0 字节
*struct{} 大小:8 字节
</pre></div>
<p>从结果可以看出:</p>
<ul><li>空结构体变量<code>struct{}</code>的内存占用为0字节,这是因为它不包含任何字段,编译时会被优化为&ldquo;零大小&rdquo;;</li><li>空结构体指针<code>*struct{}</code>的内存占用为8字节(64位架构),这是因为指针类型在特定架构下有固定大小,与指向的类型无关。</li></ul>
<p class="maodian"><a name="_lab2_2_3"></a></p><h3>3.2 struct{}的典型应用场景</h3>
<p>由于struct{}不占用内存,它常被用于以下场景:</p>
<ul><li><strong>作为map的value,表示&ldquo;集合&rdquo;</strong>:当我们只需要判断某个元素是否存在(不需要存储元素对应的value)时,用mapstruct{}比mapbool更节省内存(bool类型占用1字节,而struct{}占用0字节)。</li><li><strong>作为通道的元素,表示&ldquo;信号&rdquo;</strong>:当我们只需要通过通道传递&ldquo;事件发生&rdquo;的信号(不需要传递具体数据)时,用chan struct{}比其他类型通道更高效。</li><li><strong>作为函数返回值,表示&ldquo;无意义的结果&rdquo;</strong>:当函数只需要返回错误状态,不需要返回具体数据时,可返回<code>(struct{}, error)</code>,明确表示&ldquo;无有效返回数据&rdquo;。</li></ul>
<p class="maodian"><a name="_label3"></a></p><h2>四、~struct{}:约束含义与实践示例</h2>
<p>结合前文对~符号和struct{}的讲解,我们可以直接得出~struct{}的核心含义:<strong>约束类型参数的底层类型必须是struct{}(空结构体)</strong>。也就是说,类型参数可以是:</p>
<ul><li>原始的struct{}类型;</li><li>所有通过<code>type 新类型 struct{}</code>定义的衍生类型(底层类型为struct{})。</li></ul>
<p>下面通过多个示例代码,详细讲解~struct{}的用法、优势以及与&ldquo;精确约束struct{}&rdquo;的区别。</p>
<p class="maodian"><a name="_lab2_3_4"></a></p><h3>4.1 示例1:精确约束struct{} vs 近似约束~struct{}</h3>
<p>首先定义两个衍生自struct{}的类型,然后分别用&ldquo;精确约束struct{}&rdquo;和&ldquo;近似约束~struct{}&rdquo;定义泛型函数,观察两者的差异:</p>
<div class="jb51code"><pre class="brush:go;">package main

import "fmt"

// 定义两个底层类型为struct{}的衍生类型
type Empty1 struct{}
type Empty2 struct{}

// 精确约束:类型参数必须是struct{}
func ExactConstraint(t T) {
    fmt.Printf("ExactConstraint: 类型=%T, 大小=%d\n", t, unsafe.Sizeof(t))
}

// 近似约束:类型参数底层类型为struct{}
func ApproxConstraint(t T) {
    fmt.Printf("ApproxConstraint: 类型=%T, 大小=%d\n", t, unsafe.Sizeof(t))
}

func main() {
    // 原始struct{}类型变量
    var s struct{}
    // 衍生类型变量
    var e1 Empty1
    var e2 Empty2

    // 调用精确约束函数
    ExactConstraint(s)// 正常运行:类型=struct {}, 大小=0
    // ExactConstraint(e1) // 编译错误:Empty1 does not implement struct{} (type mismatch)
    // ExactConstraint(e2) // 编译错误:Empty2 does not implement struct{} (type mismatch)

    // 调用近似约束函数
    ApproxConstraint(s) // 正常运行:类型=struct {}, 大小=0
    ApproxConstraint(e1) // 正常运行:类型=main.Empty1, 大小=0
    ApproxConstraint(e2) // 正常运行:类型=main.Empty2, 大小=0
}
</pre></div>
<p>运行结果分析:</p>
<ul><li>精确约束函数<code>ExactConstraint</code>仅支持类型参数为原始的struct{},传入衍生类型Empty1、Empty2会直接编译错误;</li><li>近似约束函数<code>ApproxConstraint</code>支持原始struct{}和所有底层类型为struct{}的衍生类型,传入s、e1、e2均能正常运行,且所有类型的大小均为0字节(符合struct{}的内存特性)。</li></ul>
<p>这一示例清晰地体现了~符号的价值:当我们需要为&ldquo;所有空结构体衍生类型&rdquo;设计通用逻辑时,~struct{}约束是唯一的选择。</p>
<p class="maodian"><a name="_lab2_3_5"></a></p><h3>4.2 示例2:~struct{}在泛型集合中的应用</h3>
<p>前文提到,struct{}常被用作map的value表示集合。结合~struct{}约束,我们可以设计一个通用的&ldquo;空结构体类型集合&rdquo;工具,支持所有底层类型为struct{}的元素:</p>
<div class="jb51code"><pre class="brush:go;">package main

import "fmt"

// 定义衍生自struct{}的类型
type Empty struct{}
type Signal struct{}
type Flag struct{}

// 泛型集合:元素类型底层必须是struct{}
type EmptySet struct {
    items mapT // key为自定义标识,value为约束类型
}

// 初始化集合
func NewEmptySet() *EmptySet {
    return &amp;EmptySet{
      items: make(mapT),
    }
}

// 向集合中添加元素(通过key标识,value为任意~struct{}类型)
func (s *EmptySet) Add(key string, val T) {
    s.items = val
}

// 从集合中删除元素
func (s *EmptySet) Remove(key string) {
    delete(s.items, key)
}

// 判断元素是否存在
func (s *EmptySet) Exists(key string) bool {
    _, ok := s.items
    return ok
}

// 获取集合大小
func (s *EmptySet) Size() int {
    return len(s.items)
}

func main() {
    // 初始化一个存储Empty类型的集合
    emptySet := NewEmptySet()
    emptySet.Add("a", Empty{})
    emptySet.Add("b", Empty{})
    fmt.Printf("emptySet 大小:%d, 'a'是否存在:%t\n", emptySet.Size(), emptySet.Exists("a"))

    // 初始化一个存储Signal类型的集合
    signalSet := NewEmptySet()
    signalSet.Add("signal1", Signal{})
    signalSet.Remove("signal1")
    fmt.Printf("signalSet 大小:%d, 'signal1'是否存在:%t\n", signalSet.Size(), signalSet.Exists("signal1"))

    // 初始化一个存储原始struct{}类型的集合
    rawSet := NewEmptySet()
    rawSet.Add("raw1", struct{}{})
    fmt.Printf("rawSet 大小:%d, 'raw1'是否存在:%t\n", rawSet.Size(), rawSet.Exists("raw1"))
}
</pre></div>
<p>运行结果:</p>
<blockquote><p>emptySet 大小:2, &#39;a&#39;是否存在:true<br />signalSet 大小:0, &#39;signal1&#39;是否存在:false<br />rawSet 大小:1, &#39;raw1&#39;是否存在:true</p></blockquote>
<p>该示例中,我们通过~struct{}约束定义了泛型集合<code>EmptySet</code>,它可以适配Empty、Signal、Flag等所有底层类型为struct{}的衍生类型,以及原始的struct{}类型。这使得我们无需为每种衍生类型单独编写集合工具,极大地提升了代码的复用性。</p>
<p class="maodian"><a name="_lab2_3_6"></a></p><h3>4.3 示例3:~struct{}在通道信号处理中的应用</h3>
<p>结合通道和~struct{}约束,我们可以设计一个通用的信号处理器,支持处理所有&ldquo;空结构体衍生类型&rdquo;的信号:</p>
<div class="jb51code"><pre class="brush:go;">package main

import (
    "fmt"
    "time"
)

// 定义不同的信号类型(底层均为struct{})
type StartSignal struct{}
type StopSignal struct{}
type PauseSignal struct{}

// 通用信号处理器:接收任意底层为struct{}的信号
func ProcessSignal(signalChan &lt;-chan T, signalName string) {
    go func() {
      for {
            select {
            case &lt;-signalChan:
                fmt.Printf("收到信号:%s, 时间:%v\n", signalName, time.Now().Format("2006-01-02 15:04:05"))
            case &lt;-time.After(5 * time.Second):
                fmt.Printf("5秒内未收到%s信号,退出监听\n", signalName)
                return
            }
      }
    }()
}

func main() {
    // 初始化不同类型的信号通道
    startChan := make(chan StartSignal)
    stopChan := make(chan StopSignal)

    // 启动信号处理器
    ProcessSignal(startChan, "Start")
    ProcessSignal(stopChan, "Stop")

    // 发送信号
    startChan &lt;- StartSignal{}
    time.Sleep(2 * time.Second)
    stopChan &lt;- StopSignal{}

    // 等待信号处理完成
    time.Sleep(3 * time.Second)
}
</pre></div>
<p>运行结果:</p>
<blockquote><p>收到信号:Start, 时间:2025-12-04 10:00:00<br />收到信号:Stop, 时间:2025-12-04 10:00:02<br />5秒内未收到Start信号,退出监听<br />5秒内未收到Stop信号,退出监听</p></blockquote>
<p>该示例中,<code>ProcessSignal</code>函数通过~struct{}约束,实现了对所有空结构体衍生类型信号的统一处理。无论是StartSignal、StopSignal还是PauseSignal,都可以复用同一个信号处理逻辑,避免了为每种信号类型单独编写处理器的冗余代码。</p>
<p class="maodian"><a name="_label4"></a></p><h2>五、知识扩展:~符号的更多泛型约束用法</h2>
<p>~符号并非只能用于~struct{},它可以与任意类型结合使用,实现更灵活的泛型约束。下面扩展介绍~符号的其他常见用法,帮助读者举一反三。</p>
<p class="maodian"><a name="_lab2_4_7"></a></p><h3>5.1 ~与基本类型结合</h3>
<p>~可以与int、string、bool等基本类型结合,约束类型参数的底层类型为该基本类型。例如:</p>
<div class="jb51code"><pre class="brush:go;">package main

import "fmt"

// 衍生类型:底层类型为int
type MyInt int
// 衍生类型:底层类型为string
type UserID string

// 泛型函数:支持所有底层类型为int的类型
func Sum(a, b T) T {
    return a + b
}

// 泛型函数:支持所有底层类型为string的类型
func Concat(a, b T) T {
    return a + b
}

func main() {
    var a int = 10
    var b MyInt = 20
    fmt.Println(Sum(a, int(b))) // 30:MyInt可转换为int,满足~int约束

    var id1 UserID = "user_"
    var id2 string = "123"
    fmt.Println(Concat(id1, UserID(id2))) // user_123:string可转换为UserID,满足~string约束
}
</pre></div>
<p class="maodian"><a name="_lab2_4_8"></a></p><h3>5.2 ~与接口结合</h3>
<p>~可以与接口类型结合,约束类型参数实现该接口,且底层类型符合接口要求。需要注意的是,Go 1.18后接口可以包含类型约束(即&ldquo;泛型接口&rdquo;),~与接口结合时需遵循接口的约束规则。例如:</p>
<div class="jb51code"><pre class="brush:go;">package main

import "fmt"

// 定义一个接口
type Writer interface {
    Write([]byte) (int, error)
}

// 定义一个底层类型为*File的衍生类型(假设File实现了Writer接口)
type MyFile *File

// 泛型函数:支持所有底层类型实现Writer接口的类型
func WriteData(w T, data []byte) error {
    _, err := w.Write(data)
    return err
}

// 模拟File类型(实现Writer接口)
type File struct{}
func (f *File) Write(data []byte) (int, error) {
    fmt.Printf("写入数据:%s\n", string(data))
    return len(data), nil
}

func main() {
    var f *File = &amp;File{}
    var mf MyFile = &amp;File{}

    WriteData(f, []byte("hello"))// 正常运行:写入数据:hello
    WriteData(mf, []byte("world")) // 正常运行:写入数据:world
}
</pre></div>
<p class="maodian"><a name="_lab2_4_9"></a></p><h3>5.3 ~与联合约束结合</h3>
<p>~可以与联合约束(用<code>|</code>分隔多个类型)结合,约束类型参数的底层类型为联合约束中的任意一种。例如:</p>
<div class="jb51code"><pre class="brush:go;">package main

import "fmt"

// 衍生类型
type MyInt int
type MyFloat float64

// 泛型函数:支持底层类型为int或float64的类型
func Add(a, b T) T {
    return a + b
}

func main() {
    var a int = 10
    var b MyInt = 20
    var c float64 = 3.14
    var d MyFloat = 2.86

    fmt.Println(Add(a, int(b)))   // 30
    fmt.Println(Add(c, float64(d))) // 6.0
}
</pre></div>
<p class="maodian"><a name="_label5"></a></p><h2>六、实践场景: Gin 路由自动注入</h2>
<p>在gin框架的路由的自动注入中可以通过泛型 进行一个巧妙的实现,可以极大的简化代码的行数</p>
<ul><li>auto_route_inject.go</li></ul>
<div class="jb51code"><pre class="brush:go;">package router

import (
        "reflect"
        "strings"
        "unicode"

        "github.com/gin-gonic/gin"
)

/*
        路由方法介绍:
                1、GET_PingPong        请求方法:GET   接口路径:/ping/pong
                2、PingPong               请求方法:POST接口路径:/ping/pong
                3、PUT_PingPong        请求方法:PUT   接口路径:/ping/pong
*/

func routerInit2Gin(r *gin.RouterGroup, this T) {
        methodNames := getMethodNamesFromStruct(this)

        for _, methodName := range methodNames {
                // 使用局部变量捕获当前方法值
                methodVal := reflect.ValueOf(this).MethodByName(methodName)

                // 判断方法是否存在
                if !methodVal.IsValid() {
                        continue
                }

                // 创建一个匿名函数来捕获methodVal, 这里是为了防止 闭包内使用局部变量methodVal而不是循环变量
                handler := func(method reflect.Value) gin.HandlerFunc {
                        return func(c *gin.Context) {
                                // 在这里调用实际的方法
                                method.Call([]reflect.Value{reflect.ValueOf(c)})
                        }
                }(methodVal)

                // 若 methodName 以 _ 分割字符,判断 请求方法
                res := strings.Split(methodName, "_")

                switch strings.ToUpper(res) {
                case "GET":
                        if len(res) &gt;= 2 {
                                r.GET(methodNameTranstoUrl(res), handler)
                        }
                case "DELETE":
                        if len(res) &gt;= 2 {
                                r.DELETE(methodNameTranstoUrl(res), handler)
                        }

                case "PUT":
                        if len(res) &gt;= 2 {
                                r.PUT(methodNameTranstoUrl(res), handler)
                        }

                default:
                        r.POST(methodNameTranstoUrl(methodName), handler)
                }
        }
}

// 获取 结构体中的所有方法名
func getMethodNamesFromStruct(obj any) []string {
        val := reflect.ValueOf(obj)
        if val.Kind() == reflect.Ptr {
                val = val.Elem() // 如果是指针,解引用
        }

        // 确保传入的是一个结构体类型
        if val.Kind() != reflect.Struct {
                return nil
        }

        typ := val.Type()
        methodNames := make([]string, 0)

        // 遍历类型的方法集
        for i := 0; i &lt; typ.NumMethod(); i++ {
                method := typ.Method(i)
                methodNames = append(methodNames, method.Name)
        }

        return methodNames
}

// 将 方法名 转换为 GIN路由 规格的 url
func methodNameTranstoUrl(methedname string) (url string) {

        for _, char := range methedname {
                if char &gt;= 'A' &amp;&amp; char &lt;= 'Z' {
                        url += "/" + string(unicode.ToLower(char))

                } else {
                        url += string(char)
                }
        }

        return strings.TrimSpace(url)
}

</pre></div>
<p class="maodian"><a name="_label6"></a></p><h2>七、总结</h2>
<p>本文深入解析了Go泛型中~struct{}的核心特性,从基础概念出发,逐步展开为:</p>
<ul><li>~符号是Go 1.18版本随泛型同步引入的,核心目的是实现&ldquo;近似类型匹配&rdquo;,允许类型参数是约束类型的底层类型相同的衍生类型;</li><li>struct{}是&ldquo;零内存&rdquo;空结构体,常被用于集合、信号传递等场景;</li><li>~struct{}约束表示&ldquo;类型参数的底层类型为struct{}&rdquo;,支持原始struct{}和所有衍生自struct{}的类型,极大提升了泛型代码的复用性;</li><li>扩展了~符号与基本类型、接口、联合约束的结合用法,帮助读者掌握~符号的通用规律。</li></ul>
<p>在实际开发中,~struct{}约束适用于&ldquo;需要统一处理所有空结构体衍生类型&rdquo;的场景,如通用集合、通用信号处理器等。通过合理使用~符号,我们可以编写更灵活、更通用的泛型代码,充分发挥Go泛型的优势。</p>
頁: [1]
查看完整版本: Go泛型中的~struct{}的具体使用