Go 空结构体 struct{} 的使用
struct是Go中的关键字,用于定义结构类型。
例如:
type User struct {
Name string
Age int
}
struct {}
struct {}是一个无元素的结构体类型,通常在没有信息存储时使用。优点是大小为0,不需要内存来存储struct {}类型的值。
struct {} {}
struct {} {}是一个复合字面量,它构造了一个struct {}类型的值,该值也是空。
go中可以使用 unsafe.Sizeof
计算出一个数据类型实例需要占用的字节数。我们验证一下:
package main
import (
"fmt"
"unsafe"
)
func main() {
fmt.Println(unsafe.Sizeof(struct{}{}))
}
E:\go\src\learngolang\emptyStruct>go run main.go
0
也就是说空结构体实例不占用任何内存空间。
空结构体的作用
1. 实现集合set
Go 语言标准库没有提供 Set 的实现,通常使用 map 来代替。事实上,对于集合来说,只需要 map 的键,而不需要值。
声明为声明为map[string]struct{}
,由于struct{}是空,不关心内容,这样map便改造为set 。
map可以通过“comma ok”机制来获取该key是否存在,例如_, ok := map["key"]
,如果没有对应的值,ok为false。可以通过定义成map[string]struct{}
的形式,值不再占用内存。其值仅有两种状态,有或无。如果定义的是map[string]bool
,则结果有true、false或没有三种状态,而且即使是将值设置为 bool 类型,也会多占据 1 个字节。因此呢,将 map 作为集合(Set)使用时,可以将值类型定义为空结构体,仅作为占位符使用即可。
type Set map[string]struct{}
func (s Set) Has(key string) bool {
_, ok := s[key]
return ok
}
func (s Set) Add(key string) {
s[key] = struct{}{}
}
func (s Set) Delete(key string) {
delete(s, key)
}
func main() {
s := make(Set)
s.Add("Tom")
s.Add("Sam")
fmt.Println(s.Has("Tom"))
fmt.Println(s.Has("Jack"))
}
2 不发送数据的信道(channel)
基于channels发送消息有两个重要方面:发了消息、发了什么消息。一个强调了通讯的发生,一个强调了通讯的内容。当我们更希望强调通讯发生的时刻时,我们将它称为消息事件。有些消息事件并不携带额外的信息,它仅仅是用作两个goroutine之间的同步,这时候我们可以用struct{}
空结构体作为channels元素的类型。用来通知子协程(goroutine)执行任务,或只用来控制协程并发度。
3 仅包含方法的结构体
在部分场景下,结构体只包含方法,不包含任何的字段。这时候我们就可以使用空结构体。
type calculateInt struct{}
func (c calculateInt) add(x,y int) int {
return x+y
}
func (c calculateInt) sub(x,y int) int {
return x-y
}
其实,上面的calculateInt 可以是任何类型,如type calculateInt bool
,但是struct{}不占用任何空间,逻辑上也更合理,因此还是它最好。