Golang中为什么使用struct{}{}
Golang中为什么使用struct{}{} 这篇文章使用了以下结构:
done := make(chan struct{}, 2)
done <- struct{}{}
struct{} 是一种惯用法,用于表示其内容无关紧要,但其存在本身很重要吗?
它是最小的类型吗?它比 bool 或 byte 占用的空间更少吗?它的大小是零吗?
我运行了一个测试,我认为所有问题的答案都是肯定的:
package main
import (
"fmt"
)
func main() {
for i := 0; i < 100; i++ {
a := make([]struct{}, 4000000000)
fmt.Println(i, len(a))
}
}
这个程序运行得非常快。
如果我使用 byte 而不是 struct{},程序就会卡住,因为需要寻找内存。
一个大小为 0 的数组(例如 [0]bool)也具有这种零大小的特性。
更多关于Golang中为什么使用struct{}{}的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我想念Dave Cheney写的Go文章。
更多关于Golang中为什么使用struct{}{}的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你可以在这里阅读一篇关于空结构体的好文章。
是的,struct{} 是最小的类型。我喜欢把它想象成 C 语言中的 void,但它实际上是可以实例化的。它是一个结构体,但没有字段,因此不需要分配内存。它对于表示完成或就绪的信号很有用。在实现基于映射的集合类型时也很有用,例如:
setOfIntegers := make(map[int]struct{})
Go语言中的结构体,声明和创建结构体数据类型 - golangprograms.com
在本教程中,您将学习:什么是结构体、初始化结构体、嵌套结构体、为结构体创建默认值、比较结构体、理解公有和私有值、区分指针和值引用。
关于集合的观点很好。你可以这样使用它:
set := make(map[int]struct{})
set[3] = struct{}{}
if _, ok := set[3]; !ok {
t.Fail()
}
或者像这样:
type Set[T comparable] map[T]struct{}
func (s Set[T]) Put(t T) {
s[t] = struct{}{}
}
func (s Set[T]) Contains(t T) bool {
_, ok := s[t]
return ok
}
func main() {
set := make(Set[int])
set.Put(3)
fmt.Printf("%v %v\n", set.Contains(3), set.Contains(1))
fmt.Printf("%v %v\n", set[3], set[1])
}
在Go语言中,struct{}确实是一种特殊的零大小类型,常用于信号传递场景。以下是详细解释和示例:
1. 零大小特性
struct{}的大小为0,这在Go语言规范中有明确定义:
package main
import (
"fmt"
"unsafe"
)
func main() {
fmt.Println("struct{} size:", unsafe.Sizeof(struct{}{})) // 0
fmt.Println("bool size:", unsafe.Sizeof(true)) // 1
fmt.Println("byte size:", unsafe.Sizeof(byte(0))) // 1
// 零大小数组
var arr [1000000]struct{}
fmt.Println("array size:", unsafe.Sizeof(arr)) // 0
}
2. 通道中的使用
在通道中使用struct{}作为信号是最常见的用法:
package main
import (
"fmt"
"time"
)
func worker(done chan struct{}) {
time.Sleep(1 * time.Second)
done <- struct{}{} // 发送完成信号
}
func main() {
done := make(chan struct{})
go worker(done)
// 等待完成信号
<-done
fmt.Println("Worker completed")
}
3. 内存效率
零大小类型在切片和映射中特别高效:
package main
import (
"fmt"
"runtime"
)
func main() {
// 创建大量struct{}元素
set := make(map[string]struct{})
for i := 0; i < 1000000; i++ {
set[fmt.Sprintf("key%d", i)] = struct{}{}
}
// 内存使用统计
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Alloc = %v MiB\n", m.Alloc/1024/1024)
}
4. 集合实现
使用map[T]struct{}实现集合:
package main
import "fmt"
type Set map[string]struct{}
func (s Set) Add(key string) {
s[key] = struct{}{}
}
func (s Set) Contains(key string) bool {
_, ok := s[key]
return ok
}
func main() {
s := make(Set)
s.Add("apple")
s.Add("banana")
fmt.Println("Contains apple:", s.Contains("apple")) // true
fmt.Println("Contains orange:", s.Contains("orange")) // false
}
5. 与bool的比较
package main
import (
"fmt"
"unsafe"
)
func compareMemory() {
// 使用struct{}的通道
ch1 := make(chan struct{}, 1000)
// 使用bool的通道
ch2 := make(chan bool, 1000)
fmt.Printf("chan struct{} size: %d\n", unsafe.Sizeof(ch1))
fmt.Printf("chan bool size: %d\n", unsafe.Sizeof(ch2))
// 填充通道
for i := 0; i < 1000; i++ {
ch1 <- struct{}{}
ch2 <- true
}
}
6. 实际应用示例
package main
import (
"context"
"fmt"
"sync"
"time"
)
func process(ctx context.Context, wg *sync.WaitGroup, done chan struct{}) {
defer wg.Done()
select {
case <-ctx.Done():
fmt.Println("Cancelled")
case <-time.After(2 * time.Second):
fmt.Println("Process completed")
done <- struct{}{}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var wg sync.WaitGroup
done := make(chan struct{})
wg.Add(1)
go process(ctx, &wg, done)
// 等待处理完成或超时
select {
case <-done:
fmt.Println("Received completion signal")
case <-ctx.Done():
fmt.Println("Timeout reached")
}
wg.Wait()
}
struct{}在Go中确实是最小的零大小类型,主要用于信号传递和内存高效的数据结构实现。它比bool或byte更节省内存,特别是在需要大量实例的场合。

