Golang里空结构体struct{}的介绍和使用

简介: 【2月更文挑战第10天】Golang里空结构体struct{}的介绍和使用

$struct$是$Golang$里的关键字,用于定义结构类型
比如

type Student struct{
   
    id int
    name string
}

struct{}是有$0$个元素的结构体.
struct{}{}表示类型struct{}的值为空{}

1.性质

1.1不占用内存

大小为$0$,不需要内存来存储struct{}类型的值。

func Test_Size(t *testing.T) {
   
    var x int
    var y string
    var z struct{
   }
    t.Log(unsafe.Sizeof(x), unsafe.Sizeof(y), unsafe.Sizeof(z)) //8 16 0
}

1.2 地址相同

定义两个struct{}类型的变量,打印并比较其地址,发现是相同的

func Test_Address(t *testing.T) {
   
    var x struct{
   }
    var y struct{
   }
    t.Logf("%p %p %v %v", &x, &y, &x == &y, x == y) // 0x1290460 0x1290460 true true
}

2.用途

2.1实现set

golang里没有实现set,如果在使用map的时候只关心key,不关心value的话,可以借助struct{}来实现set。比如key为string类型,那么set的实现map[string]struct{}。
因为struct{}不占用空间,所以在查找和判断的过程中速度会快,而且占用的内存也会小
简单实现:

func Test_set(t *testing.T) {
   
    set := make(map[string]struct{
   })
    set["a"] = struct{
   }{
   }
    if _, ok := set["a"]; ok {
   
        t.Log("exists") //exists
    }
}

使用interface{}实现

type Set map[interface{
   }]struct{
   }

func (s Set) Add(item interface{
   }) {
   
    s[item] = struct{
   }{
   }
}

func (s Set) Remove(item interface{
   }) {
   
    delete(s, item)
}

func (s Set) Contains(item interface{
   }) bool {
   
    _, exists := s[item]
    return exists
}

func Test_any_set(t *testing.T) {
   
    set := make(Set)

    set.Add("apple")
    set.Add("banana")
    set.Add("orange")

    fmt.Println("Set:", set)

    fmt.Println("Contains apple:", set.Contains("apple"))
    fmt.Println("Contains grape:", set.Contains("grape"))

    set.Remove("banana")

    fmt.Println("Set:", set)
}

使用泛型实现:

type Set[T comparable] map[T]struct{
   }

func (s Set[T]) Add(v T) {
   
    s[v] = struct{
   }{
   }
}

func (s Set[T]) Remove(v T) {
   
    delete(s, v)
}

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

func (s Set[T]) Len() int {
   
    return len(s)
}

func (s Set[T]) Values() []T {
   
    values := make([]T, 0, s.Len())
    for v := range s {
   
        values = append(values, v)
    }
    return values
}

func Test_any_set(t *testing.T) {
   
    s := Set[string]{
   }
    s.Add("apple")
    s.Add("banana")
    s.Add("orange")

    fmt.Println("Set:", s.Values())

    fmt.Println("Contains apple:", s.Contains("apple"))
    fmt.Println("Contains grape:", s.Contains("grape"))

    s.Remove("banana")

    fmt.Println("Set:", s.Values())
}

2.2. 用于无数据的channel

有的时候$channel$不需要发送数据,只需要一个触发信号,就可以使用struct{}来减少信号传递过程中的内存开销
a. 等待协程完成
$worker$函数是一个协程,它会模拟一些工作并在完成后发送空结构体值到$done$通道。
在$Test_wait$函数中,我们通过从$done$通道接收空结构体值来等待工作完成。

func worker(done chan struct{
   }) {
   
    fmt.Println("Worker: Performing some work...")
    time.Sleep(2 * time.Second)
    fmt.Println("Worker: Work completed!")
    done <- struct{
   }{
   } // 发送空结构体值表示工作完成
}
func Test_wait(t *testing.T) {
   
    done := make(chan struct{
   })
    go worker(done)
    <-done // 接收空结构体值,等待工作完成
    t.Log("Main: Done!")
}

b.触发事件
$waitForEvent$函数会等待接收到空结构体值,表示事件发生。
$triggerEvent$函数会在一段时间后发送空结构体值到event通道,表示事件发生。
通过使用空结构体值作为通道元素,我们可以简单地实现事件的触发和等待。

func waitForEvent(event chan struct{
   }) {
   
    fmt.Println("Waiting for event...")
    <-event // 等待接收到空结构体值,表示事件发生
    fmt.Println("Event received!")
}

func triggerEvent(event chan struct{
   }) {
   
    time.Sleep(2 * time.Second)
    fmt.Println("Triggering event...")
    event <- struct{
   }{
   } // 发送空结构体值,表示事件发生
}
func Test_event(t *testing.T) {
   
    event := make(chan struct{
   })
    go waitForEvent(event)
    go triggerEvent(event)
    time.Sleep(3 * time.Second)
}

2.3 方法接收器

实现一个接口,只需要实现一些方法,不用声明一些额外的数据,可以用struct{}来实现,也可以换成其他任意的变量,比如$int$ ,$bool$

type Animal interface {
   
    Shouting()
}
type Dog struct{
   }

func (dog *Dog) Shouting() {
   
    fmt.Println("wang wang wang")
}
目录
相关文章
Golang反射---结构体的操作案例大全
Golang反射---结构体的操作案例大全
73 0
|
2月前
|
JSON Go 数据格式
Golang语言结构体链式编程与JSON序列化
这篇文章是关于Go语言中结构体链式编程与JSON序列化的教程,详细介绍了JSON格式的基本概念、结构体的序列化与反序列化、结构体标签的使用以及如何实现链式编程。
39 4
|
2月前
|
Go
Golang语言结构体(struct)面向对象编程进阶篇(封装,继承和多态)
这篇文章是关于Go语言中结构体(struct)面向对象编程进阶篇的教程,涵盖了Go语言如何实现封装、继承和多态,以及结构体内存布局的相关概念和案例。
144 4
|
2月前
|
Go
Golang语言结构体(struct)面向对象编程基础篇
这篇文章是关于Go语言中结构体(struct)面向对象编程的基础教程,详细介绍了面向对象编程在Go语言中的应用、结构体的定义与初始化、方法定义、跨包实例化结构体以及结构体方法和普通函数的区别。
31 4
|
6月前
|
JSON Go 数据格式
golang学习7,glang的web的restful接口结构体传参
golang学习7,glang的web的restful接口结构体传参
|
6月前
|
Go 开发者
Golang深入浅出之-Go语言结构体(struct)入门:定义与使用
【4月更文挑战第22天】Go语言中的结构体是构建复杂数据类型的关键,允许组合多种字段。本文探讨了结构体定义、使用及常见问题。结构体定义如`type Person struct { Name string; Age int; Address Address }`。问题包括未初始化字段的默认值、比较含不可比较字段的结构体以及嵌入结构体字段重名。避免方法包括初始化结构体、自定义比较逻辑和使用明确字段选择器。结构体方法、指针接收者和匿名字段嵌入提供了灵活性。理解这些问题和解决策略能提升Go语言编程的效率和代码质量。
119 1
|
6月前
|
JSON 编译器 Go
Golang深入浅出之-结构体标签(Tags):JSON序列化与反射应用
【4月更文挑战第22天】Go语言结构体标签用于添加元信息,常用于JSON序列化和ORM框架。本文聚焦JSON序列化和反射应用,讨论了如何使用`json`标签处理敏感字段、实现`omitempty`、自定义字段名和嵌套结构体。同时,通过反射访问标签信息,但应注意反射可能带来的性能问题。正确使用结构体标签能提升代码质量和安全性。
292 0
|
2月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
111 4
Golang语言之管道channel快速入门篇
|
2月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
66 4
Golang语言文件操作快速入门篇
|
2月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
101 3
Golang语言之gRPC程序设计示例