惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
美团技术团队
H
Hacker News: Front Page
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tenable Blog
Simon Willison's Weblog
Simon Willison's Weblog
T
The Exploit Database - CXSecurity.com
Cisco Talos Blog
Cisco Talos Blog
A
Arctic Wolf
C
CXSECURITY Database RSS Feed - CXSecurity.com
Application and Cybersecurity Blog
Application and Cybersecurity Blog
A
About on SuperTechFans
F
Fortinet All Blogs
量子位
GbyAI
GbyAI
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The Hacker News
The Hacker News
AWS News Blog
AWS News Blog
Forbes - Security
Forbes - Security
Help Net Security
Help Net Security
I
InfoQ
有赞技术团队
有赞技术团队
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
Engineering at Meta
Engineering at Meta
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
S
Secure Thoughts
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Webroot Blog
Webroot Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园_首页
C
Check Point Blog
T
Troy Hunt's Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Latest news
Latest news
P
Proofpoint News Feed
Jina AI
Jina AI
Last Week in AI
Last Week in AI
Martin Fowler
Martin Fowler
雷峰网
雷峰网
博客园 - Franky
L
LangChain Blog
罗磊的独立博客
Blog — PlanetScale
Blog — PlanetScale
Google DeepMind News
Google DeepMind News
D
Docker
G
GRAHAM CLULEY
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

Nemo

再见,2025 Complete ORB-SLAM3 Setup Guide for Jetson Xavier NX with RealSense D455 20250723 再见,2024 Using CertBot for Automatic Secure EMQX Broker Create Your Own GPS Data Publisher Support SSL AGV Dispatching System Technical Documentation Finding Nemo No title 香港賽馬會呈獻系列:黑白——攝影敘事 再见,2023 团团是只猫 Design an FSM for Robot State Machines – Basics of Computer Science Data-driven robot lifespan: Collection 数据驱动的机器人寿命:收集、诊断、预测 Exploring the design space of binary search trees 特首来了 Large language models, explained with a minimum of math and jargon 设计有缓存异步逻辑的监控脚本并测试其资源占用 使用loguru记录串口数据并使用Docker搭建ARM开发环境 在vscode的Dev Container中构建.NET开发环境及使用doxygen和graphviz绘制函数调用图 State or Status? A*算法两种时间复杂度 /A* Algorithm: Two Types of Time Complexity 使用KD-Tree快速收敛到最近坐标点/Fast convergence to the nearest coordinate point using KD-Tree 再见,2022 从PE工作报告中能读出什么 Give me miles, give me truth AMR调度系统性能优化/AMR Dispatch System Performance Optimization 条件触发AMR避让流程/Conditionally triggered AMR avoidance process Docker实现调度系统整体部署/Docker implementation of dispatching system overall deployment 调度系统中加密算法的使用/增加SM4补0方法/Use of encryption algorithms in scheduling systems/add SM4 complementary 0 method 一篇关于北京四天三夜的攻略 2022 藏疆自驾 策划/招募书 AMR仿真模型/AMR Simulation Model 东东有鱼2022年会分享 使用perf-FlameGraph监控系统性能 Beyond Compare 4删除试用 你的灵魂有香气 Pyinstaller打包Python项目 [转载]Python中的单例模式的几种实现方式的及优化 - Nemo 再见! 2020 1024 UML软件建模 入职优必选一个月 写论文时 那些让你开心的软件 - Nemo 分享一个记录刷题次数的模版 LeetCode-查找表类算法题精析 PyCharm 调用vs 2010 C++库导致提示报错R6034解决方法 MySQL基础教程 多种数据结构的Python实现形式 字节跳动-挑战字符串 选择排序 二分查找和大O表示法 算法中的动态规划问题 高高手课程-青山裕企人像摄影 笔记(内含福利)
翻译 || 总结 - Go语言中的空结构体(The empty struct)
Nemo · 2023-04-04 · via Nemo

翻译 || 总结 – Go语言中的空结构体(The empty struct)

总结

在Go语言中,结构体是一种用户自定义的数据类型。通常情况下,结构体中包含一个或多个字段,每个字段都有一个对应的数据类型。但是,Go语言也支持定义一个不包含任何字段的结构体,也就是所谓的空结构体。

为什么要定义一个空结构体呢?因为在Go语言中,变量的类型也是其内存占用的大小。如果一个变量不需要存储任何数据,那么它所占用的内存大小就可以为0。而定义一个空结构体正好可以满足这个需求。例如,可以将一个空结构体作为某个通道的元素类型,这样就可以实现一个类似信号量的功能。

空结构体的定义非常简单,只需要使用 struct{} 即可。需要注意的是,空结构体虽然不包含任何字段,但是它仍然是一种类型,因此可以定义变量、类型别名、方法等。

这篇文章还讨论了空结构体在Go语言中的一些应用场景。其中一个非常常见的应用场景是用空结构体作为某些数据类型的占位符。比如,当我们需要存储一些与数据本身无关的元数据时,可以使用空结构体来表示这个元数据。

另外一个应用场景是使用空结构体作为函数的参数或返回值。这种用法可以让函数返回一个没有实际意义的值,但是可以方便地判断函数的执行结果。例如,标准库中的 sync 包中的 WaitGroup 类型就是通过空结构体作为参数来实现的。

最后,作者还讨论了一些关于空结构体的性能优化的问题。因为空结构体的大小为0,所以在某些情况下,使用空结构体可以提高程序的运行效率。但是,在其他情况下,空结构体可能会带来一些不必要的性能开销。因此,在使用空结构体时,需要根据具体的应用场景进行权衡和选择。

最后,空结构体虽然看起来非常简单,但是在Go语言中具有非常重要的应用价值。通过使用空结构体,我们可以实现一些特殊的功能,并且在一定程度上提高程序的运行效率。

另外,空结构体还有一个有趣的应用,即用于实现类似 Set 的数据结构。在 Go 语言中,map 可以用于实现 Set,但是如果只是想存储元素的存在与否,使用 map 可能会带来一定的性能开销。而使用空结构体作为 Set 中的元素类型,可以避免这个问题。因为空结构体不占用内存空间,所以存储元素的开销很小,同时也能保证元素的唯一性。例如:

type Set map[string]struct{}

func (s Set) Add(str string) {
    s[str] = struct{}{}
}

func (s Set) Contains(str string) bool {
    _, ok := s[str]
    return ok
}

在上面的例子中,Set 类型实际上是一个 map,但是它的 value 类型是 struct{},也就是空结构体。在 Add 方法中,我们直接将空结构体作为 map 的 value,而在 Contains 方法中,我们只需要判断 map 是否存在指定的 key 即可。这样就实现了一个高效、轻量级的 Set 数据结构。

除了上面介绍的应用场景之外,空结构体还可以用于实现某些特殊的数据结构,例如链表、树等。此外,空结构体还可以用于实现某些并发模式,例如信号量、锁等。总之,空结构体虽然看起来非常简单,但是在 Go 语言中具有非常重要的应用价值。通过使用空结构体,我们可以实现一些特殊的功能,并且在一定程度上提高程序的效率和性能。

需要注意的是,使用空结构体也存在一些需要注意的地方。首先,如果一个变量的类型是空结构体,那么它的值永远都是相同的,因为空结构体不包含任何字段。因此,如果使用空结构体定义一个全局变量,那么所有使用该变量的代码都会共享同一个实例。如果在并发环境中使用空结构体,需要特别小心,以避免出现数据竞争等问题。

另外,空结构体并不是 Go 语言中唯一的占用 0 字节内存的类型。例如,[0]byte 也是一种占用 0 字节内存的类型。不过,相比于 [0]byte,空结构体更加灵活和易用,因此在实际开发中使用更为广泛。

在 Go 语言中,空结构体是一个非常特殊的类型。虽然它看起来非常简单,但是在实际开发中却有着非常重要的应用价值。通过使用空结构体,我们可以实现一些特殊的功能,提高程序的效率和性能。当然,在使用空结构体时需要特别小心,以避免出现不必要的问题。

博客原文

The empty struct | Dave Cheney https://dave.cheney.net/2014/03/25/the-empty-struct,原文结尾有中文版链接,但是打不开,就自己翻译一遍,水平不高,见谅

Introduction

This post explores the properties of my favourite Go data type, the empty struct.

The empty struct is a struct type that has no fields. Here are a few examples in named and anonymous forms

type Q struct{}
var q struct{}

So, if an empty struct contains no fields, contains no data, what use is it ? What can we do with it ?

Width

Before diving into the empty struct itself, I wanted to take a brief detour to talk about width.

The term width comes, like most terms, from the gc compiler, although its etymology probably goes back decades.

Width describes the number of bytes of storage an instance of a type occupies. As a process’s address space is one dimensional, I think width is a more apt than size.

Width is a property of a type. As every value in a Go program has a type, the width of the value is defined by its type and is always a multiple of 8 bits.

We can discover the width of any value, and thus the width of its type using the unsafe.Sizeof() function.

var s string
var c complex128
fmt.Println(unsafe.Sizeof(s))    // prints 8
fmt.Println(unsafe.Sizeof(c))    // prints 16

http://play.golang.org/p/4mzdOKW6uQ

The width of an array type is a multiple of its element type.

var a [3]uint32
fmt.Println(unsafe.Sizeof(a)) // prints 12

http://play.golang.org/p/YC97xsGG73

Structs provide a more flexible way of defining composite types, whose width is the sum of the width of the constituent types, plus padding

type S struct {
        a uint16
        b uint32
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 8, not 6

The example above demonstrates one aspect of padding, that a value must be aligned in memory to a multiple of its width. In this case there are two bytes of padding added by the compiler between a and b.

Update Russ Cox has kindly written to explain that width is unrelated to alignment. You can read his comment below.

Now that we’ve explored width it should be evident that the empty struct has a width of zero. It occupies zero bytes of storage.

var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0

As the empty struct consumes zero bytes, it follows that it needs no padding. Thus a struct comprised of empty structs also consumes no storage.

type S struct {
        A struct{}
        B struct{}
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 0

http://play.golang.org/p/PyGYFmPmMt

What can you do with an empty struct

True to Go’s orthogonality, an empty struct is a struct type like any other. All the properties you are used to with normal structs apply equally to the empty struct.

You can declare an array of structs{}s, but they of course consume no storage.

var x [1000000000]struct{}
fmt.Println(unsafe.Sizeof(x)) // prints 0

http://play.golang.org/p/0lWjhSQmkc

Slices of struct{}s consume only the space for their slice header. As demonstrated above, their backing array consumes no space.

var x = make([]struct{}, 1000000000)
fmt.Println(unsafe.Sizeof(x)) // prints 12 in the playground

http://play.golang.org/p/vBKP8VQpd8

Of course the normal subslice, len, and cap builtins work as expected.

var x = make([]struct{}, 100)
var y = x[:50]
fmt.Println(len(y), cap(y)) // prints 50 100

http://play.golang.org/p/8cO4SbrWVP

You can take the address of a struct{} value, when it is addressable, just like any other value.

var a struct{}
var b = &a

Interestingly, the address of two struct{} values may be the same.

var a, b struct{}
fmt.Println(&a == &b) // true

http://play.golang.org/p/uMjQpOOkX1

This property is also observable for []struct{}s.

a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println(&a == &b)       // false, a and b are different slices
fmt.Println(&a[0] == &b[0]) // true, their backing arrays are the same

http://play.golang.org/p/oehdExdd96

Why is this? Well if you think about it, empty structs contain no fields, so can hold no data. If empty structs hold no data, it is not possible to determine if two struct{} values are different. They are in effect, fungible.

a := struct{}{} // not the zero value, a real new struct{} instance
b := struct{}{}
fmt.Println(a == b) // true

http://play.golang.org/p/K9qjnPiwM8

note: this property is not required by the spec, but it does note that Two distinct zero-size variables may have the same address in memory.

struct{} as a method receiver

Now we’ve demonstrated that empty structs behave just like any other type, it follows that we may use them as method receivers.

type S struct{}

func (s *S) addr() { fmt.Printf("%p\\n", s) }

func main() {
        var a, b S
        a.addr() // 0x1beeb0
        b.addr() // 0x1beeb0
}

http://play.golang.org/p/YSQCczP-Pt

In this example it shows that the address of all zero sized values is 0x1beeb0. The exact address will probably vary for different versions of Go.

Wrapping up

Thank you for reading this far. At close to 800 words this article turned out to be longer than expected, and there was still more I was planning to write.

While this article concentrated on language obscura, there is one important practical use of empty structs, and that is the chan struct{} construct used for signaling between go routines

I’ve talked about the use of chan struct{} in my Curious Channels article.

Translations

Update Damian Gryski pointed out that I had omitted Brad Fitzpatrick’s [iter](http://godoc.org/github.com/bradfitz/iter) package. I’ll leave it as an exercise to the reader to explore the profound implications of Brad’s contribution.

翻译

介绍

这篇博客将探索我最喜欢的Go语言的数据结构——空struct

空struct就是没有字段的结构,下面是命名和匿名形式的表示:

type Q struct{}
var q struct{}

那么,如果一个空结构不包含字段,不包含数据,它有什么用呢?我们能用它做什么?

宽度(width该怎么翻译?)

在深入研究空结构本身之前,我想绕道而行,谈谈宽度。

与大多数术语一样,术语宽度来自 gc 编译器,尽管它的词源可能可以追溯到几十年前。

宽度描述类型实例占用的存储字节数。由于进程的地址空间是一维的,我认为宽度比大小更合适。

宽度是类型的属性。由于 Go 程序中的每个值都有一个类型,值的宽度由其类型定义,并且始终是 8 位的倍数。https://en.wikipedia.org/wiki/8-bit_computing

我们可以使用 unsafe.Sizeof() 函数发现任何值的宽度,从而发现其类型的宽度。

var s string
var c complex128
fmt.Println(unsafe.Sizeof(s))    // prints 8
fmt.Println(unsafe.Sizeof(c))    // prints 16

http://play.golang.org/p/4mzdOKW6uQ

数组类型的宽度是其元素类型的倍数。

var a [3]uint32
fmt.Println(unsafe.Sizeof(a)) // prints 12

http://play.golang.org/p/YC97xsGG73

结构体提供了一种更灵活的方式来定义复合类型,其宽度是构成类型的宽度加上填充的总和

type S struct {
        a uint16
        b uint32
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 8, not 6

上面的示例演示了填充的一个方面,即一个值必须在内存中对齐为其宽度的倍数。在这种情况下,编译器在 a 和 b 之间添加了两个字节的填充。

Update Russ Cox has kindly written to explain that width is unrelated to alignment. You can read his comment below.

空结构

现在我们已经探索了宽度,应该很明显空结构的宽度为零。它占用零字节的存储空间。

var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0

由于空结构消耗零字节,因此它不需要填充。因此,由空结构组成的结构也不消耗存储空间。

type S struct {
        A struct{}
        B struct{}
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 0

http://play.golang.org/p/PyGYFmPmMt

空结构的使用

与 Go 的正交性一样,空结构是一种与其他结构类型一样的结构类型。普通结构的所有属性同样适用于空结构。

可以声明一个 structs{} 数组,但它们当然不占用存储空间,即使个数很多

var x [1000000000]struct{}
fmt.Println(unsafe.Sizeof(x)) // prints 0

http://play.golang.org/p/0lWjhSQmkc

struct{} 的切片仅占用其切片标头的空间。如上所示,他们的后备阵列不占用空间。

var x = make([]struct{}, 1000000000)
fmt.Println(unsafe.Sizeof(x)) // prints 12 in the playground

当然,正常的 subslice、len 和 cap 内置函数可以按预期工作。

var x = make([]struct{}, 100)
var y = x[:50]
fmt.Println(len(y), cap(y)) // prints 50 100

可以获取 struct{} 值的地址,当它可寻址时,就像任何其他值一样。

var a struct{}
var b = &a

有趣的是,两个 struct{} 值的地址可能是相同的。

var a, b struct{}
fmt.Println(&a == &b) // true

此属性对于 []struct{} 也是可见的。

a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println(&a == &b)       // false, a and b are different slices
fmt.Println(&a[0] == &b[0]) // true, their backing arrays are the same

为什么是这样?因为空结构不包含任何字段,因此不能容纳任何数据。如果空结构不包含任何数据,则无法确定两个结构{} 值是否不同。它们实际上是可替代的。

a := struct{}{} // not the zero value, a real new struct{} instance
b := struct{}{}
fmt.Println(a == b) // true

note: this property is not required by the spec, but it does note that Two distinct zero-size variables may have the same address in memory.

空结构作为函数接收器

现在我们已经证明了空结构的行为就像任何其他类型一样,因此我们可以将它们用作方法接收器。

type S struct{}

func (s *S) addr() { fmt.Printf("%p\n", s) }

func main() {
        var a, b S
        a.addr() // 0x1beeb0
        b.addr() // 0x1beeb0
}

在此示例中,它显示所有大小为零的值的地址为 0x1beeb0。不同版本的 Go 的确切地址可能会有所不同。

Related Posts:

  1. Struct composition with Go
  2. Friday pop quiz: the smallest buffer
  3. Simple extended attribute support for Go
  4. Padding is hard