定义的各种姿势
原生定义
a := struct{}{}
1
struct{} 可以就认为是一种类型,a 变量就是 struct {} 类型的一种变量,地址为 runtime.zerobase ,大小为 0 ,不占内存。
重定义类型
golang 使用 type 关键字定义新的类型,比如:
type emptyStruct struct{}
1
定义出来的 emptyStruct 是新的类型,具有对应的 type 结构,但是性质 struct{} 完全一致,编译器对于 emptryStruct 类型的内存分配,也是直接给 zerobase 地址的。
————————————————
var s struct{}
变量 size 是 0 ; fmt.Println(unsafe.Sizeof(s))
本质上来讲,使用空结构体的初衷只有一个:节省内存,但是更多的情况,节省的内存其实很有限,这种情况使用空结构体的考量其实是:根本不关心结构体变量的值。
作用1 :做set,map[string]struct{}
由于struct{}是空,不关心内容,这样map便改造为set
set := make(map[string]struct{})
for _, value := range []string{"apple", "orange", "apple"} {
set[value] = struct{}{}
}
fmt.Println(set)
// Output: map[orange:{} apple:{}]
map 和 struct {} 一般的结合姿势是这样的:
// 创建 map
m := make(map[int]struct{})
// 赋值
m[1] = struct{}{}
// 判断 key 键存不存在
_, ok := m[1]
一般 map 和 struct {} 的结合使用场景是:只关心 key,不关注值。比如查询 key 是否存在就可以用这个数据结构,通过 ok 的值来判断这个键是否存在,map 的查询复杂度是 O(1) 的,查询很快。
你当然可以用 map[int]bool 这种类型来代替,功能也一样能实现,很多人考虑使用 map[int]struct{} 这种使用方式真的就是为了省点内存,当然大部分情况下,这种节省是不足道哉的,所以究竟要不要这样使用还是要看具体场景。
作用2:chan struct{}
可以做退出
func worker(i int, ch chan Work, quit chan struct{}) {
for {
select {
case w := <-ch:
// do something
case <-quit:
fmt.Println("worker", i, "quitting")
quit = nil
}
}
}
还有其他情况,比如有时候使用channel,但并不需要附带任何数据。
func worker(ch chan struct{}) {
// Receive a message from the main program.
<-ch
println("roger")
// Send a message to the main program.
close(ch)
}
func main() {
ch := make(chan struct{})
go worker(ch)
// Send a message to a worker.
ch <- struct{}{}
// Receive a message from the worker.
<-ch
println(“roger")
// Output:
// roger
// roger
}
chan & struct{}
channel 和 struct{} 结合是一个最经典的场景,struct{} 通常作为一个信号来传输,并不关注其中内容。chan 的分析在前几篇文章有详细说明。chan 本质的数据结构是一个管理结构加上一个 ringbuffer ,如果 struct{} 作为元素的话,ringbuffer 就是 0 分配的。
chan 和 struct{} 结合基本只有一种用法,就是信号传递,空结构体本身携带不了值,所以也只有这一种用法啦,一般来说,配合 no buffer 的 channel 使用。
// 创建一个信号通道
waitc := make(chan struct{})
// ...
goroutine 1:
// 发送信号: 投递元素
waitc <- struct{}
// 发送信号: 关闭
close(waitc)
goroutine 2:
select {
// 收到信号,做出对应的动作
case <-waitc:
}
这种场景我们思考下,是否一定是非 struct{}
不可?其实不是,而且也不多这几个字节的内存,所以这种情况真的就只是不关心 chan
的元素值而已,所以才用的 struct{}
。